332 lines
24 KiB
HTML
332 lines
24 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
|||
|
<title>ActivityPub plugin - Tech Report - GNU social Summer of Code 2018</title>
|
|||
|
<link rel="icon" href="../../favicon.png">
|
|||
|
<meta charset="utf-8">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|||
|
<link rel="stylesheet" href="https://www.diogo.site/projects/excalibur_template/assets/css/main.css">
|
|||
|
<style>
|
|||
|
@page {
|
|||
|
size: A4 portraint;
|
|||
|
}
|
|||
|
|
|||
|
@page :blank {
|
|||
|
@top-center { content: "This page is intentionally left blank." }
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
h1 {
|
|||
|
page-break-before: always;
|
|||
|
}
|
|||
|
|
|||
|
h1, h2, h3, h4, h5 {
|
|||
|
page-break-after: avoid;
|
|||
|
}
|
|||
|
|
|||
|
table, figure {
|
|||
|
page-break-inside: avoid;
|
|||
|
}
|
|||
|
|
|||
|
@page:right{
|
|||
|
@bottom-right {
|
|||
|
content: "Page " counter(page) " of " counter(pages);
|
|||
|
}
|
|||
|
}
|
|||
|
</style>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<header id="header">
|
|||
|
<nav id="side-menu">
|
|||
|
<label for="show-menu" id="menu-button">Menu</label>
|
|||
|
<input id="show-menu" role="button" type="checkbox">
|
|||
|
<ul id="menu">
|
|||
|
<li><a href="../"><strong>← GS GSoC</strong></a></li>
|
|||
|
<li><a href="#about">About</a></li>
|
|||
|
<li><a href="#activitypub">ActivityPub</a></li>
|
|||
|
<li><a href="#plugin-workflow">Plugin Workflow</a></li>
|
|||
|
<li><a href="#inboxes">Inboxes</a></li>
|
|||
|
<li><a href="#collections">Collections</a></li>
|
|||
|
<li><a href="#acknowledgements">Acknowledgements</a></li>
|
|||
|
<li><a href="#testimonials">Testimonials</a></li>
|
|||
|
</ul>
|
|||
|
</nav>
|
|||
|
<h1>ActivityPub plugin for GNU social</h1>
|
|||
|
<p>Developed by <strong><a href="https://www.diogo.site/">Diogo Cordeiro</a></strong> during <strong><a href="https://summerofcode.withgoogle.com/archive/2018/projects/5289876471676928/">Google Summer of Code 2018</a></strong></p>
|
|||
|
<p>Mentored by <a href="https://github.com/dansup">Daniel Supernault</a> and <a href="https://mmn-o.se/">Mikael Nordfeldth</a></p>
|
|||
|
</header>
|
|||
|
<article id="about">
|
|||
|
<h1><a href="#about">About the project and this document</a></h1>
|
|||
|
<h2>What is this document?</h2>
|
|||
|
<p>This is a technical Google Summer of Code Project Report describing
|
|||
|
briefly what I did from April 23 to August 6.
|
|||
|
</p>
|
|||
|
<p>The whole code I wrote during this period is available
|
|||
|
<a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2">here</a>.
|
|||
|
The API documentation is available
|
|||
|
<a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377">here</a>.
|
|||
|
</p>
|
|||
|
<p>This document helps understanding what was done in a simple and informal way,
|
|||
|
highlighting the most relevant parts, why some decisions were made and what
|
|||
|
I've learned with it.
|
|||
|
</p>
|
|||
|
<p>The following document is intended to be reasonably self contained
|
|||
|
and capable of quickstarting anyone in the world of ActivityPub implementation,
|
|||
|
yet not replacing the official standard specification.
|
|||
|
</p>
|
|||
|
<p>From April 23 to May 14 I had to familiarize myself with the
|
|||
|
Activity Pub standard and GNU social's plugins/events API and community.
|
|||
|
</p>
|
|||
|
<h2>What is GNU social?</h2>
|
|||
|
<p>GNU social is a social communication software used in federated
|
|||
|
social networks.
|
|||
|
</p>
|
|||
|
<h2>ActivityPub?</h2>
|
|||
|
<p>In order to achieve said decentralization, a variety of standards on
|
|||
|
how communication between different pieces of software in a federated
|
|||
|
context should be done were created, such as OStatus. <a href="https://www.w3.org/TR/activitypub/">ActivityPub</a>, however,
|
|||
|
is the newest and covers parts left out of OStatus's specification, namely
|
|||
|
app/client development.
|
|||
|
</p>
|
|||
|
<h3>Benefits</h3>
|
|||
|
<ul>
|
|||
|
<li>The tendency is that newer software will focus in the
|
|||
|
implementation of the ActivityPub Protocol (as it is newer
|
|||
|
and considered to be simpler) instead of OStatus, given
|
|||
|
that, it is important for GNU social to support it in order to stay updated
|
|||
|
and relevant in an even larger <a href="https://en.wikipedia.org/wiki/Fediverse">fediverse</a>;
|
|||
|
</li>
|
|||
|
<li>It will simplify app/client development which is not covered by the
|
|||
|
OStatus's specification and is becoming more and more relevant,
|
|||
|
with the increase of smartphone users.
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
<h2>Why a plugin?</h2>
|
|||
|
<h3 id="unix-tools-design-philosophy">Unix tools design philosophy</h3>
|
|||
|
<p>GNU social is true to the Unix-philosophy of small programs to do a
|
|||
|
small job.
|
|||
|
</p>
|
|||
|
<blockquote>
|
|||
|
<ul>
|
|||
|
<li>Compact and concise input syntax, making full use of ASCII repertoire to minimise keystrokes</li>
|
|||
|
<li>Output format should be simple and easily usable as input for other programs</li>
|
|||
|
<li>Programs can be joined together in “pipes” and “scripts” to solve more complex problems</li>
|
|||
|
<li>Each tool originally performed a simple single function</li>
|
|||
|
<li>Prefer reusing existing tools with minor extension to rewriting a new tool from scratch</li>
|
|||
|
<li>The main user-interface software (“shell”) is a normal replaceable program without special privileges</li>
|
|||
|
<li>Support for automating routine tasks</li>
|
|||
|
</ul>
|
|||
|
<footer>- Brian W. Kernighan, Rob Pike: The Unix Programming Environment. Prentice-Hall, 1984.</footer>
|
|||
|
</blockquote>
|
|||
|
<p>As so the project aims at building a plugin that will
|
|||
|
implement the ActivityPub Protocol in GNU social.
|
|||
|
</p>
|
|||
|
</article>
|
|||
|
<article id="activitypub">
|
|||
|
<h1><a href="#activitypub">So how does ActivityPub work?</a></h1>
|
|||
|
<p>The ActivityPub protocol is a decentralized social networking
|
|||
|
protocol based upon the <a href="https://www.w3.org/TR/activitystreams-core/">ActivityStreams</a> 2.0 data format.
|
|||
|
It provides a client to server API for creating, updating and deleting
|
|||
|
content, as well as a federated server to server API for delivering
|
|||
|
notifications and content.
|
|||
|
</p>
|
|||
|
<img src="https://www.w3.org/TR/activitypub/illustration/tutorial-2.png">
|
|||
|
<blockquote>
|
|||
|
<p><strong>Note:</strong> You are advised to read (at least) the <a href="https://www.w3.org/TR/activitypub/#Overview">standard
|
|||
|
overview</a> before continuing.
|
|||
|
</p>
|
|||
|
</blockquote>
|
|||
|
<h2>Actor</h2>
|
|||
|
<p>Actor is the user doing a change in the fediverse and are identified by an URI.
|
|||
|
In GNU social they have the following format:
|
|||
|
<code>https://myinstance.net/user/{id}</code>. Thus allowing users to change their
|
|||
|
usernames (or nicknames).
|
|||
|
</p>
|
|||
|
<h2 id="objects">Objects</h2>
|
|||
|
<p>Objects are our building blocks for the bigger concept of Activity.
|
|||
|
The plugin has the following objects:
|
|||
|
</p>
|
|||
|
<ul>
|
|||
|
<li>Actors are defined by the <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#profile">profile object</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_profile.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Error">Error</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_error.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Tag">Tag</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_tag.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Mention">Mention Tag</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_mention_tag.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Notice">Notice</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_notice.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Attachment">Attachment</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_attachment.php">here</a>.</li>
|
|||
|
</ul>
|
|||
|
<blockquote>
|
|||
|
<p><strong>Note:</strong> Tags, Attachment and Image are ("minor")
|
|||
|
objects and, usually, wrapped by ("major") objects.
|
|||
|
</p>
|
|||
|
</blockquote>
|
|||
|
<h2>Activities</h2>
|
|||
|
<p>Wrappers allow to give additional information on a given object.
|
|||
|
The implemented wrappers are the following:
|
|||
|
</p>
|
|||
|
<ul>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Create">Create</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_create.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Announce">Announce</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_announce.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Delete">Delete</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_delete.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Undo">Undo</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_undo.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Accept">Accept</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_accept.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Reject">Reject</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_reject.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Like">Like</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_like.php">here</a>.</li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin-doc/src/7188c0039793969417b4be912615125658f69377/objects_and_activities.md#Follow">Follow</a> which can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_follow.php">here</a>.</li>
|
|||
|
</ul>
|
|||
|
<p>By wrapping an <a href="#objects">object</a> with one of those we are
|
|||
|
able to perform an action in name of a given Actor.
|
|||
|
</p>
|
|||
|
</article>
|
|||
|
<article id="plugin-workflow">
|
|||
|
<h1><a href="#plugin-workflow">And how does the plugin work?</a></h1>
|
|||
|
<p>For the plugin I decided to create two key components.</p>
|
|||
|
<h2 id="explorer">The Explorer</h2>
|
|||
|
<p>Defined in this <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/utils/explorer.php">file</a> it is responsable for grabbing remote users and creating an identity for those in the local instance. It is higly related with <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_profile.php">Activitypub_profile.php</a>.</p>
|
|||
|
<h2 id="postman">The Postman</h2>
|
|||
|
<p>This guy does the magic of <code>publishing</code> the <code>activities</code>
|
|||
|
(thus the name ActivityPub!) to remote instances, he kinda is the
|
|||
|
responsable for the federation provided by this plugin. Its code is <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/utils/postman.php">here</a>.
|
|||
|
</p>
|
|||
|
<h3>Becoming familiar with GNU social's events</h3>
|
|||
|
<p>We just know what and when to deliver something thanks to <a href="https://git.gnu.io/gnu/gnu-social/blob/nightly/EVENTS.txt">GNU social's events</a>.</p>
|
|||
|
<h4>Events the plugin is handling</h4>
|
|||
|
<div class="tasks">
|
|||
|
<h5>Discovery Events</h5>
|
|||
|
<ul>
|
|||
|
<li>Find remote profiles from mentions in texts (<em>onEndFindMentions)</em></li>
|
|||
|
<li>Allow to reference a remote profile (<em>onStartCommandGetProfile)</em></li>
|
|||
|
<li>Allow to reference a remote profile (<em>onStartGetProfileUri)</em></li>
|
|||
|
</ul>
|
|||
|
<h5>Delivery Events</h5>
|
|||
|
<ul>
|
|||
|
<li>Notify remote instance of subscription (<em>onEndSubscribe</em>)</li>
|
|||
|
<li>Notify remote instance when a subscription is canceled (<em>onEndUnsubscribe</em>)</li>
|
|||
|
<li>Notify remote instances when a notice is liked (<em>onEndFavorNotice</em>)</li>
|
|||
|
<li>Notify remote instances when a notice like is reverted (<em>onEndDisfavorNotice</em>)</li>
|
|||
|
<li>Notify remote instances when a notice is deleted (<em>onEndDeleteOwnNotice</em>)</li>
|
|||
|
<li>Notify remote instances when a notice is created (<em>onStartNoticeDistribute</em>)</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<p>These are not the only events this plugin is listening to, but are
|
|||
|
the most relevant. All these events hooks can be found <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/ActivityPubPlugin.php">here</a>.
|
|||
|
</p>
|
|||
|
</article>
|
|||
|
<article id="inboxes">
|
|||
|
<h1><a href="#inboxes">Hold on, if you have a Postman then there sure is an inbox, right?</a></h1>
|
|||
|
<p>Two actually! :)</p>
|
|||
|
<p>Fediverse is similar to the real world in some aspects. And one of those
|
|||
|
is that people, sometimes, share the same "building". A list of some
|
|||
|
public GNU social instances is available <a href="https://gnu.io/social/try/">here</a>.
|
|||
|
</p>
|
|||
|
<p>Well, if two people live in the same place then our <a href="#postman">Postman</a> doesn't have to "travel" twice! Thus
|
|||
|
the concept of <code><a href="https://www.w3.org/TR/activitypub/#shared-inbox-delivery">shared inbox</a></code>.
|
|||
|
</p>
|
|||
|
<p>Lets start with the <a href="https://www.w3.org/TR/activitypub/#inbox">inbox</a>. An
|
|||
|
inbox is where we, I mean, the <a href="#postman">Postman</a> publishes
|
|||
|
our Activity.
|
|||
|
</p>
|
|||
|
<p>By now you should have already realized that Activities are just
|
|||
|
verbs wrapping objects thus basically forming a phrase:
|
|||
|
<code>Actor</code> <code>verb</code> <code>Object</code> <code>to Attention Actor(s)</code>.
|
|||
|
</p>
|
|||
|
<p>Well, for each <code>Attention actor</code> we deliver the same
|
|||
|
<code>Actor</code> <code>verb</code> <code>Object</code>.
|
|||
|
</p>
|
|||
|
<p>Since it is the same message and frequently for people residing
|
|||
|
in the same server we can obviously reduce a lot of traffic and
|
|||
|
processing power here.
|
|||
|
</p>
|
|||
|
<p>The <a href="#postman">Explorer</a> hands the <a href="#postman">Postman</a> the sharedInbox address always that
|
|||
|
it is possible.
|
|||
|
</p>
|
|||
|
<ul>
|
|||
|
<li>
|
|||
|
<p>Inbox Handler code is <a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/utils/inbox_handler.php">here</a>.</p>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</article>
|
|||
|
<article id="collections">
|
|||
|
<h1><a href="#collections">Our plugin also is a collector</a></h1>
|
|||
|
<p>The following collections have been implemented:</p>
|
|||
|
<div class="tasks">
|
|||
|
<ul>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/actions/apactorfollowers.php">Followers</a></li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/actions/apactorfollowing.php">Following</a></li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/actions/apactorliked.php">Liked</a></li>
|
|||
|
<li><a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/actions/apactoroutbox.php">Outbox</a></li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<p>Collections are lists. And lists are very important in the world of
|
|||
|
fediverse both for <a href="https://www.w3.org/TR/activitypub/#server-to-server-interactions">Server
|
|||
|
to Server</a> and <a href="https://www.w3.org/TR/activitypub/#client-to-server-interactions">Client
|
|||
|
to Server</a>.
|
|||
|
</p>
|
|||
|
</article>
|
|||
|
<article id="todo">
|
|||
|
<h1><a href="#todo">What is left to do?</a></h1>
|
|||
|
<p>This plugin fulfills and even exceeds what I've originally proposed to
|
|||
|
do during this summer (hooray!). But there is still room for further development
|
|||
|
and improvement!
|
|||
|
</p>
|
|||
|
<h2>Todo</h2>
|
|||
|
<ul>
|
|||
|
<li>Finish implementing Client to Server</li>
|
|||
|
<li>Implement a Circuit Breaker</li>
|
|||
|
<li>Redis caching of collections</li>
|
|||
|
<li>Queues</li>
|
|||
|
</ul>
|
|||
|
</article>
|
|||
|
<article id="left-out">
|
|||
|
<h1><a href="#left-out">So, that was it?</a></h1>
|
|||
|
<p>Not really, this document doesn't describe the implementation of
|
|||
|
<a href="https://notabug.org/diogo/gnu-social-activitypub-plugin/src/6fbf37b7fee8c73271a47089a4f4a70304cf15a2/classes/Activitypub_rsa.php">
|
|||
|
HTTP Signatures</a>, the internal representation of ActivityPub Actors,
|
|||
|
nor the details of Activity Streams or even the particularities of integrating
|
|||
|
this in GNU social (I'm looking at you OStatus plugin). As stated in the
|
|||
|
beginning of this document, the whole code and documentation is available
|
|||
|
above, this was just meant to make the reading easier.
|
|||
|
</p>
|
|||
|
<h2>I'm interested in implementing ActivityPub on my program and reading
|
|||
|
this really helped me! Is there something else that I should know of that
|
|||
|
wasn't mentioned in the article?
|
|||
|
</h2>
|
|||
|
<p>Be sure to know <a href="https://www.restapitutorial.com/httpstatuscodes.html">HTTP Status Codes</a>
|
|||
|
and to start soon with the <a href="https://en.wikipedia.org/wiki/Test-driven_development">Test-driven development</a>.
|
|||
|
</p>
|
|||
|
<p>And, obviously, follow rigorously the ActivityPub specification!</p>
|
|||
|
<p>If you need help you can <a href="https://www.diogo.site/#contact">contact me</a>
|
|||
|
or Daniel Supernault directly or, even better, join the
|
|||
|
<a href="irc://irc.w3.org:6665/social">W3C social IRC channel (#social@irc.w3.org)</a>,
|
|||
|
you will definitely find us both there! :)
|
|||
|
</p>
|
|||
|
<article id="acknowledgements">
|
|||
|
<h1><a href="#acknowledgements">Final Words</a></h1>
|
|||
|
<p>GSoC was a wonderful experience for me. I now feel more comfortable
|
|||
|
with the GNU social’s codebase. I learned a lot of useful stuff like
|
|||
|
ActivityPub, ActivityStreams, HTTP Signatures, PSR and Redis as well as
|
|||
|
software engineering concepts like Circuit Breakers. I've also learned
|
|||
|
more about Git and how libre and open source software development is
|
|||
|
done and organized.
|
|||
|
</p>
|
|||
|
<p>I will try my best to regularly contribute to GNU social and other
|
|||
|
projects.
|
|||
|
</p>
|
|||
|
<p>Thanks to Daniel Supernault and Mikael Nordfeldth for such a
|
|||
|
wonderful experience and the knowledge.
|
|||
|
</p>
|
|||
|
</article>
|
|||
|
<article id="testimonials">
|
|||
|
<h1><a href="https://www.diogo.site/testimonials.html">Testimonials</a></h1>
|
|||
|
<blockquote>
|
|||
|
<p style="margin:0">You're active, persistent and productive. Keep it up and we'll all have a great summer of code!</p>
|
|||
|
<footer>- Mikael Nordfeldth, GNU social Maintainer (2018) on my first GSoC evaluation</footer>
|
|||
|
</blockquote>
|
|||
|
<a href="https://mastodon.social/@dansup/100331648394320885"><img src="images/testimonial1.png"></a>
|
|||
|
<a href="https://mastodon.social/@dansup/100353565526207146"><img src="images/testimonial2.png"></a>
|
|||
|
<a href="https://mastodon.social/@dansup/100416797627457542"><img src="images/testimonial3.png"></a>
|
|||
|
</article>
|
|||
|
</article>
|
|||
|
</body>
|
|||
|
</html>
|