What if there was a social media network that couldn’t ban you? Imagine if Twitter couldn’t delete your post history and social graph, sometimes built over decades, over a single post they disagree with. If the idea of decentralized social media intrigues you, then you need to try the Fediverse. Basically, it’s a network of different social media apps that let you create your own server and link up with other servers. Sort of like how email lets you create an account on any service, but you can still message with users on the other services. In this article, we’re going to teach how you can start coding Fediverse apps with Javascript and never worry again about Big Tech terminating your accounts.
But first, we’ll cover the basic architecture of federated apps. The Fediverse is built on an underlying protocol called ActivityPub which enables servers to link up and communicate. Although we’ll link to resources for learning ActivityPub, it’s more important for now to understand the basic architecture.
After that, we’ll get to the best part – building our own federated app completely from scratch.
How the Fediverse works
Suppose that you have a new social media network, which we’ll call FreeFrens. At first, there was only one server that users were able to join, called freefrens.example. But then, you allow other servers to join the network. Eventually, a second server joins in, called, morefrens.test. Now, when users on FreeFrens check their feed, they’ll see posts from both FreeFrens and MoreFrens!
When a MoreFrens user likes a FreeFrens post, the morefrens.test server lets the FreeFrens server know, and the like will show up on both services!
You could have an open network that any server can join, or you can only federate with predefined servers. The idea is, you can set up your own server, or join one with rules that appeal to you. MoreFrens can’t “ban” FreeFrens users from the network, they can only ban them from one place – MoreFrens. A server can ban users or other servers, but not from the network!
But enough theory, let’s start building something!
Let’s start Fediverse coding!
We’re going to make a simple federated app where anyone can post a message. When you access your wall, you see not only messages from your server, but also every server you federate with. For simplicity, each server will be a single user, so we don’t have to worry about our server managing different user accounts and such.
We’ll start with a simple API in JavaScript, using the Express.js framework.
mkdir postiverse
cd postiverse
npm init
npm install express
Now we’re almost ready to write some code! We just need to think about what features our API should have.
- Check messages
- Post messages
- User profiles
- And all the normal boilerplate required by Express
The skeleton looks like this:
const express = require('express');
const app = express();
const PORT = 3000;
app.use(express.json());
app.use(cors());
const peers = ['127.0.0.1:3002']
const posts = []
const profile = {
avatar: '/my_avatar.jpeg',
name: 'Some cool dude',
bio: 'i love censorship resistant tech'
}
const domain = 'localhost'
app.get('/posts', async (req, res) => {
// return the `posts` variable.
});
app.post('/posts', (req, res) => {
// create a new post});
app.get('/profile', (_req, res) => {
// return the `profile` variable
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Now we just fill in these endpoints one by one!
We’ll start with the endpoint to get posts.
app.get('/posts', async (req, res) => {
try {
let allPosts = [];
for (const hostname of peers) {
const response = await axios.get(`http://${hostname}/posts`);
allPosts = allPosts.concat(response.data).concat(posts);
}
res.json(allPosts);
} catch (error) {
console.error('Error fetching data:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
The original comment just said to return the `posts` variable. But in fact, we want to access posts from servers we federate with as well! Moving on, we next must allow the frontend to upload new posts to the local server.
app.post('/posts', (req, res) => {
const { post } = req.body;
posts.push({ date: new Date().toLocaleString(), post, from: `${profile.name}@${domain}` })
console.log(posts)
return res.status(201).json(
{ success: true, message: 'Post published successfully' });
});
Pretty straightforward! In the “real world”, we’d store this in a database. For simplicity, though, we just store everything in memory. And finally, the profile:
app.get('/profile', (_req, res) => {
res.json(profile)
});
That’s all, the API is done! The frontend can be pretty simple – we just to create an interface for this API. Given my experience with React, I chose that, but you could just as easily stick with vanilla JS.
Building a web-based UI
Here’s the main code for the front page:
import './App.css';
import { useState, useEffect } from 'react';
import Post from './components/Post';
import Submit from './components/Submit';
const sortedDates = (a, b) => {
const dateA = new Date(a.date);
const dateB = new Date(b.date);
return dateB - dateA;
};
function App() {
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('http://localhost:3000/posts');
const result = await response.json();
setPosts(result.sort(sortedDates));
} catch (error) {
console.error('Error fetching data:', error);
}
};
const interval = setInterval(fetchData, 500);
return () => clearInterval(interval);
}, []);
return (
<div className="App">
<header>
<h1>
Welcome to postiverse!
</h1>
A federated posting app.
<Submit />
</header>
<section style={{marginTop: '30px', color: 'darkgray'}} id='posts'>
{(posts.length === 0)
? "No posts to show..."
: posts.map(p => Post({...p}))
}
</section>
<hr />
</div>
);
}
export default App;
To reiterate: the frontend is very simple, and you can write these components however you want. It’s only a matter of style and taste.
If you want to copy my components exactly, you can access them here: https://github.com/darighost/postiverse.
To make sure federation works, we should run two instances of the server. Once we have both the frontend and backend up and running, the frontend should look something like this:
Barebones, but it works! Note that we see posts from two servers: Boiiii@127.0.01 and Darigo@localhost.
Great! Feel free to add features, or even make a pull request on the Github repo. We’ll keep adding features, so it might have even more functionality by the time you’re reading this.
Exploring the Fediverse
We kept the code above simple by using our own protocol from scratch. In reality, the majority of fediverse apps rely on a protocol called ActivityPub. You can learn more about the modern ActivityPub driven fediverse by visiting fediverse.party, where you can explore an interactive map of the ActivityPub based Fediverse.
A few other notable mentions with regards to federation include the Matrix chat protocol, and Urbit, which is a federated OS as well as cultural phenomenon.
Finally, there’s the “original” fediverse of protocols like SMPT, IRC, and Usenet.
Learn more about Fediverse coding
Federation is an abstract concept that can be implemented in a bunch of different ways. However, if you want your apps to be able to integrate with other apps in the modern Fediverse, you basically have two choices:
- ActivityPub
- Urbit
If you want to learn Urbit, they have some of the best docs of any tech stack, and you can check them out here: https://docs.urbit.org/
In practice, though, when most people today use the term “Fediverse”, they really mean ActivityPub. So let’s talk about how you can learn Fediverse coding with ActivityPub.
First, before getting into the weeds by creating an entirely new platform, I recommend setting up a personal server and installing an existing Fediverse platform. Get a DigitalOcean droplet or EC2 server and install PeerTube or Mastodon. Try federating with some other servers. Get a few users. Besides, a personal server has many uses that are great for learning hacking.
This will give you a hands-on appreciation for the fun (and terror!) of being an admin for a federated server.
Then, once you’re ready, you should learn how to create ActivityPub apps by following the fantastic advice in this guide: activitypub.rocks.
Although apps like Mastodon use tens of thousands of lines of code, you can start with simpler services that only offer modest feature sets. The key is to get started!
Leave a Reply