547 lines
18 KiB
Org Mode
547 lines
18 KiB
Org Mode
:PROPERTIES:
|
|
:ID: 20221202T122017.620403
|
|
:ROAM_REFS: https://akkoma.dev/AkkomaGang/akkoma https://akkoma.social/
|
|
:END:
|
|
#+TITLE: Self-Hosting on the Fediverse with Akkoma
|
|
#+FILETAGS: :Akkoma Social:
|
|
#+ARCOLOGY_KEY: cce/akkoma
|
|
|
|
Akkoma is a [[id:62538db5-d94a-47c3-9998-086ded91fd88][Fediverse]]/[[id:activitypub][ActivityPub]] server forked from [[roam:Pleroma]] written in [[id:cce/elixir][Elixir]], supporting the [[id:339daa8c-cc01-4654-aa89-330a4e62aafa][Mastodon Server]] API. This is a light-weight thing and I intend to self-publish to the Fediverse with an instance running on [[id:20211120T220054.226284][The Wobserver]].
|
|
|
|
* [[https://github.com/NixOS/nixpkgs/pull/192285][akkoma: init at 3.4.0 by illdefined · Pull Request #192285 · NixOS/nixpkgs]]
|
|
:PROPERTIES:
|
|
:ROAM_REF: [[https://github.com/NixOS/nixpkgs/pull/192285]]
|
|
:ID: 20221202T122230.525913
|
|
:END:
|
|
[2022-12-02 Fri 12:22]
|
|
|
|
* Akkoma on [[id:c75d20e6-8888-4c5a-ac97-5997e2f1c711][NixOS]]
|
|
:PROPERTIES:
|
|
:ID: 20221202T122135.502628
|
|
:ROAM_ALIASES: "Akkoma Server"
|
|
:END:
|
|
:LOGBOOK:
|
|
CLOCK: [2022-12-02 Fri 12:22]--[2022-12-02 Fri 16:24] => 4:02
|
|
:END:
|
|
|
|
The configuration interface in NixOS is nicer but also quite complicated. I have this broken up a bit and it consists of *most* of the configuration but not things like emoji and some other static assets like my favicon and whatnot.
|
|
|
|
It's not super complicated but we'll break it up in to multiple imports so that I can explain what is going on a bit:
|
|
|
|
#+ARROYO_SYSTEM_ROLE: server
|
|
#+ARROYO_NIXOS_MODULE: nixos/akkoma.nix
|
|
=myAkkoma= carries an [[https://akkoma.dev/AkkomaGang/akkoma/commit/af7c3fab98f4f5d1fa541035fd8b2821e0abb77b][unreleased patch]] to skip over the Pleroma observability rules -- i need to fix the [[id:20220101T190353.843667][Wobservability]] section below to use this anyways, but I couldn't turn off the observability rules because they were in the DB configuration. Oughtta make sure I move that stuff out in to =config.exs= fairly often.
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma.nix :noweb yes
|
|
{ config, pkgs, lib, ... }:
|
|
|
|
let
|
|
myAkkoma = pkgs.akkoma.overrideAttrs (old: old // {
|
|
# patches = [
|
|
# (<arroyo/files/akkoma-fix-pleroma-migration.patch>)
|
|
# ];
|
|
});
|
|
in{
|
|
imports = [
|
|
./akkoma-users.nix
|
|
./akkoma-statics.nix
|
|
./akkoma-frontends.nix
|
|
./akkoma-virtualhost.nix
|
|
./akkoma-wobservability.nix
|
|
./akkoma-pwa.nix
|
|
./akkoma-config.nix
|
|
./akkoma-mrf.nix
|
|
./akkoma-search.nix
|
|
];
|
|
|
|
services.postgresql.ensureDatabases = ["akkoma"];
|
|
# have to run psql for migrations to pass:
|
|
# ALTER DATABASE akkoma OWNER TO akkoma;
|
|
services.postgresql.ensureUsers = [
|
|
{
|
|
name = "akkoma";
|
|
ensurePermissions = {
|
|
"DATABASE akkoma" = "ALL PRIVILEGES";
|
|
};
|
|
}
|
|
];
|
|
|
|
services.akkoma = {
|
|
enable = true;
|
|
package = myAkkoma;
|
|
|
|
group = "akkoma";
|
|
user = "akkoma";
|
|
};
|
|
}
|
|
#+end_src
|
|
|
|
** Akkoma Service Configuration
|
|
|
|
ref [[https://docs.akkoma.dev/stable/configuration/cheatsheet/][Configuration Cheat Sheet]], none of this is particularly exciting.
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma-config.nix
|
|
{ config, pkgs, ... }:
|
|
{
|
|
services.akkoma.extraPackages = with pkgs; [exiftool ffmpeg-headless imagemagick];
|
|
# don't feel like setting password._secret here...
|
|
services.akkoma.initDb.enable = false;
|
|
services.akkoma.config = {
|
|
":pleroma".":instance" = {
|
|
name = "Computers :(";
|
|
description = "rrix's fediverse home";
|
|
email = "fedi@whatthefuck.computer";
|
|
registrations_open = false;
|
|
limit = 5000;
|
|
local_bubble = [
|
|
"botsin.space"
|
|
"hackers.town"
|
|
"tenforward.social"
|
|
"regenerate.social"
|
|
];
|
|
export_prometheus_metrics = true;
|
|
|
|
static_dir = "/srv/akkoma/static";
|
|
upload_dir = "/srv/akkoma/uploads";
|
|
};
|
|
|
|
":pleroma".":static_fe".enabled = true;
|
|
|
|
":pleroma"."Pleroma.Repo" = {
|
|
adapter = (pkgs.formats.elixirConf { }).lib.mkRaw "Ecto.Adapters.Postgres";
|
|
username = config.services.akkoma.user;
|
|
database = "akkoma";
|
|
hostname = "localhost";
|
|
timeout = 30000;
|
|
|
|
prepare = (pkgs.formats.elixirConf { }).lib.mkRaw ":named";
|
|
parameters = {
|
|
plan_cache_mode = "force_custom_plan";
|
|
};
|
|
};
|
|
|
|
":pleroma".":configurable_from_database" = true;
|
|
|
|
":pleroma".":media_proxy" = {
|
|
enabled = true;
|
|
proxy_opts.redirect_on_failure = true;
|
|
};
|
|
|
|
":pleroma"."Pleroma.Upload".filters =
|
|
map (pkgs.formats.elixirConf { }).lib.mkRaw [
|
|
"Pleroma.Upload.Filter.Exiftool"
|
|
"Pleroma.Upload.Filter.Dedupe"
|
|
"Pleroma.Upload.Filter.AnonymizeFilename"
|
|
];
|
|
|
|
":pleroma"."Pleroma.Web.Endpoint" = {
|
|
http = {
|
|
ip = "127.0.0.1";
|
|
port = 4000;
|
|
};
|
|
url = {
|
|
host = "notes.whatthefuck.computer";
|
|
scheme = "https";
|
|
port = 443;
|
|
};
|
|
};
|
|
|
|
# secrets
|
|
":pleroma"."Pleroma.Repo".password._secret = "/srv/akkoma/dbpass";
|
|
":joken".":default_signer"._secret = "/srv/akkoma/jwt-signer";
|
|
":pleroma"."Pleroma.Web.Endpoint" = {
|
|
live_view.signing_salt._secret = "/srv/akkoma/liveview-salt";
|
|
secret_key_base._secret = "/srv/akkoma/secret-key-base";
|
|
signing_salt._secret = "/srv/akkoma/signing-salt";
|
|
};
|
|
":web_push_encryption".":vapid_details" = {
|
|
private_key._secret = "/srv/akkoma/vapid-private";
|
|
public_key._secret = "/srv/akkoma/vapid-public";
|
|
subject = "mailto:fedi@whatthefuck.computer";
|
|
};
|
|
};
|
|
}
|
|
#+end_src
|
|
|
|
** System Users
|
|
|
|
I really would like to manage my uids and gids better, but alas.
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma-users.nix
|
|
{ config, pkgs, ... }:
|
|
|
|
{
|
|
ids.uids.akkoma = 901;
|
|
ids.gids.akkoma = 901;
|
|
|
|
users.groups.akkoma = {
|
|
gid = config.ids.gids.akkoma;
|
|
};
|
|
|
|
users.users.akkoma = {
|
|
group = "akkoma";
|
|
uid = config.ids.uids.akkoma;
|
|
shell = "${pkgs.bash}/bin/bash";
|
|
isSystemUser = true;
|
|
# ugh... services.pleroma.stateDir is readonly
|
|
home = "/var/lib/pleroma";
|
|
createHome = true;
|
|
};
|
|
}
|
|
#+end_src
|
|
|
|
** Frontend Management
|
|
|
|
so if you set a =static_dir= to a mutable directory on the server, the NixOS module won't install the frontends you ask for. This is good and fine, and not a surprise, certainly not undocumented. So what if you just:
|
|
=pleroma_ctl frontend install admin-fe --ref stable=
|
|
=pleroma_ctl frontend install fedibird-fe --ref akkoma=
|
|
=pleroma_ctl frontend install pleroma-fe --ref stable=
|
|
|
|
That fails because =BindReadOnlyPaths= is set in the NixOS module as part of the hardening of the service. I shouldn't mind, but =** (File.Error) could not make directory (with -p) "/srv/akkoma/static/frontends/tmp": read-only file system=
|
|
|
|
so, okay, make sure =BindReadOnlyPaths= is not set, and set =ReadWritePaths= just to be sure.
|
|
In theory it'd be possible to use a =system.activationScripts= entry to take =frontends.$foo.package= and slam a symlink in like I do for the terms of service above, but i'm tired.
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma-frontends.nix
|
|
{ config, pkgs, lib, ... }:
|
|
|
|
{
|
|
systemd.services.akkoma.serviceConfig.ReadWritePaths = [ config.services.akkoma.config.":pleroma".":instance".static_dir ];
|
|
systemd.services.akkoma.serviceConfig.BindReadOnlyPaths = lib.mkForce [ ];
|
|
services.akkoma.frontends = {
|
|
primary = {
|
|
name = "pleroma-fe";
|
|
ref = "stable";
|
|
};
|
|
mastodon= {
|
|
name = "fedibird-fe";
|
|
ref = "akkoma";
|
|
};
|
|
admin = {
|
|
name = "admin-fe";
|
|
ref = "stable";
|
|
};
|
|
};
|
|
|
|
# shove this thing in a system.activationScript to symlink to static_dir/frontends
|
|
# frontends.primary.package = pkgs.runCommand "akkoma-fe" {
|
|
# config = builtins.toJSON {
|
|
# expertLevel = 1;
|
|
# collapseMessageWithSubject = false;
|
|
# replyVisibility = "following";
|
|
# hideScopeNotice = true;
|
|
# renderMisskeyMarkdown = false;
|
|
# hideSiteFavicon = true;
|
|
# postContentType = "text/markdown";
|
|
# showNavShortcuts = false;
|
|
# };
|
|
# nativeBuildInputs = with pkgs; [ jq xorg.lndir ];
|
|
# passAsFile = [ "config" ];
|
|
# } ''
|
|
# mkdir $out
|
|
# lndir ${pkgs.akkoma-frontends.akkoma-fe} $out
|
|
#
|
|
# rm $out/static/config.json
|
|
# jq -s add ${pkgs.akkoma-frontends.akkoma-fe}/static/config.json ${config} \
|
|
# >$out/static/config.json
|
|
# '';
|
|
}
|
|
#+end_src
|
|
|
|
** Nginx Frontend for Akkoma
|
|
|
|
Nothing special here -- I have it split in to two blocks here because one of my old iterations of [[id:1d917282-ecf4-4d4c-ba49-628cbb4bb8cc][The Arcology Project]] used this domain to host short-form [[id:fa6cee69-dca0-45f5-ae9e-b71cad3702a6][IndieWeb]]/microformat notes. The old files still exist and can be resolved in the =try_files= block, and any failures will proxy through to the app backend. I also adjust the max body size for image uploads, etc. I might replace that with S3 in the future but for now the images can just go on to the file system.
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma-virtualhost.nix
|
|
{ ... }:
|
|
|
|
{
|
|
services.nginx.virtualHosts."notes.whatthefuck.computer" = {
|
|
root = "/srv/static-sites/notes.whatthefuck.computer/_site";
|
|
extraConfig = ''
|
|
client_max_body_size 100M;
|
|
'';
|
|
locations."/".extraConfig = ''
|
|
try_files $uri @proxy;
|
|
'';
|
|
locations."@proxy" = {
|
|
proxyPass = "http://127.0.0.1:4000";
|
|
extraConfig = ''
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header X-Forwarded-Host $http_host;
|
|
'';
|
|
};
|
|
locations."/phoenix/live_dashboard".extraConfig = ''
|
|
auth_basic "closed site";
|
|
auth_basic_user_file /etc/nginx-htpasswd;
|
|
'';
|
|
};
|
|
}
|
|
#+end_src
|
|
|
|
** Static Files
|
|
|
|
I could just splat this on to the filesystem but no harm in having it in the Nix store:
|
|
|
|
#+begin_src html :noweb-ref tos
|
|
<p>
|
|
Now look; this is a single-user
|
|
instance. <a href="https://notes.whatthefuck.computer/rrix">rrix</a>
|
|
posts inane bullshit here. Look at their profile if you care about
|
|
what is going on here.
|
|
</p>
|
|
|
|
<p>
|
|
<ul>
|
|
<li>I'm not a fascist.</li>
|
|
<li>I'm not a cop.</li>
|
|
<li>I'm not a narc.</li>
|
|
<li>I'm not a racist.</li>
|
|
<li>I'm not a transphobe.</li>
|
|
<li>I'm not gonna put up with bullshit.</li>
|
|
<li>I'm just a little computer goblin who wants to self-host.</li>
|
|
</ul>
|
|
</p>
|
|
|
|
<p>
|
|
If you care about the privacy policy of this instance, don't
|
|
federate with it. rrix is a consummate privacy professional, but
|
|
they're also just one person. I have no intention to do anything
|
|
untoward with posts federated to my instance, nor engage in
|
|
non-standard behavior on the fediverse, the NixOS code which deploys
|
|
all the software on this server
|
|
is <a href="https://cce.whatthefuck.computer/akkoma">available
|
|
online</a>.
|
|
</p>
|
|
|
|
<p>
|
|
At the same time, I'm likely not going to be able to go
|
|
up against government requests for data stored on this instace. As
|
|
of [2023-02-16] this instance has not been compelled to give data to
|
|
any government or law enforcement agency and has not done so
|
|
voluntarily. I'm just one homie hanging out making posts with my
|
|
friends and trying to make new ones, and you're here reading
|
|
this. What's up?
|
|
</p>
|
|
#+end_src
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma-statics.nix :noweb yes
|
|
{ config, pkgs, ... }:
|
|
|
|
let
|
|
tos = pkgs.writeTextFile {
|
|
name="pleroma-terms-of-service";
|
|
text = ''
|
|
<<tos>
|
|
'';
|
|
};
|
|
in
|
|
{
|
|
# services.akkoma.extraStatic."static/terms-of-service.html" = tos;
|
|
system.activationScripts.install-pleroma-tos.text = ''
|
|
echo "Installing Pleroma Terms of Service to static directory"
|
|
export DEST_DIR=/srv/akkoma/static/
|
|
mkdir -p $DEST_DIR
|
|
ln -sf ${tos} $DEST_DIR/static/terms-of-service.html
|
|
'';
|
|
}
|
|
|
|
#+end_src
|
|
|
|
** [[id:20220101T190353.843667][Wobservability]]
|
|
|
|
I would like to have some usage metrics emitted, this is just service-level stuff:
|
|
|
|
Enable =Pleroma.Web.Endpoint.MetricsExporter= in settings.
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma-wobservability.nix
|
|
{ ... }:
|
|
|
|
{
|
|
services.prometheus.scrapeConfigs = [
|
|
{
|
|
job_name = "akkoma";
|
|
metrics_path = "/api/pleroma/app_metrics";
|
|
static_configs = [{ targets = [ "127.0.0.1:4000" ]; }];
|
|
}
|
|
];
|
|
}
|
|
#+end_src
|
|
|
|
** PWA manifest for Pleroma-FE
|
|
|
|
Pleroma's mute filters, etc aren't 1-1 compatible with Mastodon API so they don't work in Mastodon-FE or Subway Tooter... This is ...unfortunate. Let's see how using Pleroma-FE as a [[https://developer.mozilla.org/en-US/docs/Web/Manifest][Progressive Web App]] goes..
|
|
|
|
#+begin_src json :tangle ~/arroyo-nix/files/notes-pwa.json
|
|
{
|
|
"$schema": "https://json.schemastore.org/web-manifest-combined.json",
|
|
"name": "Fedi Notes",
|
|
"short_name": "Fedi Notes",
|
|
"start_url": "https://notes.whatthefuck.computer/",
|
|
"display": "standalone",
|
|
"background_color": "#004daa",
|
|
"theme_color": "#31363b",
|
|
"description": "Pleroma-FE on notes.whatthefuck.computer",
|
|
"icons": [{
|
|
"src": "https://notes.whatthefuck.computer/media/e8ecd309a5e3d99736beff056fb461ba157dc4ff9317f3cc6063c4ce280bd604.blob",
|
|
"sizes": "312x312",
|
|
"type": "image/png"
|
|
}]
|
|
}
|
|
#+end_src
|
|
|
|
This needs to be shoved in to =<head>= and there's no way to do that directly in Pleroma-FE so I make a silly little HTML page and shove it on to a virtualhost:
|
|
|
|
#+begin_src web :tangle ~/arroyo-nix/files/notes-pwa.html
|
|
<html>
|
|
<head>
|
|
<link rel="manifest" href="notes-pwa.json" />
|
|
</head>
|
|
<body>
|
|
<h1>Pleroma-FE as PWA</h1>
|
|
<p>
|
|
On browsers which support "add to home screen" or similar
|
|
things, you can create a Progressive Web App which will
|
|
load the <code>Computer :(</code> site in a dedicated frame.
|
|
</p>
|
|
</body>
|
|
</html>
|
|
#+end_src
|
|
|
|
And this is installed, like so:
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma-pwa.nix
|
|
{ config, pkgs, ... }:
|
|
|
|
{
|
|
system.activationScripts = let
|
|
json = <arroyo/files/notes-pwa.json>;
|
|
html = <arroyo/files/notes-pwa.html>;
|
|
in {
|
|
install-pleroma-tos.text = ''
|
|
echo "Installing Pleroma PWA manifest and static page"
|
|
export DEST_DIR=/srv/static-sites/default
|
|
mkdir -p $DEST_DIR
|
|
ln -sf ${html} $DEST_DIR/pwa.html
|
|
ln -sf ${json} $DEST_DIR/notes-pwa.json
|
|
'';
|
|
};
|
|
services.nginx.virtualHosts."fontkeming.fail".extraConfig = ''
|
|
disable_symlinks off;
|
|
'';
|
|
}
|
|
#+end_src
|
|
|
|
## why 404?
|
|
|
|
** Enable =meilisearch= for Full Text Search of Toots
|
|
|
|
The Postgres FTS in Pleroma seemed like it worked better than the one in Akkoma? Very strange, to me. =meilisearch= is an elasticsearch-alike that is hopefully less shitty than elasticsearch.
|
|
|
|
This is configured based on [[https://docs.akkoma.dev/stable/configuration/search/#meilisearch][the docs]], and Akkoma's secret handling...
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma-search.nix
|
|
{ config, pkgs, lib, ... }:
|
|
|
|
{
|
|
services.meilisearch = {
|
|
enable = true;
|
|
noAnalytics = true;
|
|
masterKeyEnvironmentFile = "/srv/meilisearch/key.txt";
|
|
environment = "production";
|
|
};
|
|
|
|
services.akkoma.config = {
|
|
":pleroma"."Pleroma.Search" = {
|
|
module = (pkgs.formats.elixirConf { }).lib.mkRaw "Pleroma.Search.Meilisearch";
|
|
};
|
|
":pleroma"."Pleroma.Search.Meilisearch" = {
|
|
url = "http://127.0.0.1:7700";
|
|
private_key._secret = "/srv/akkoma/meilisearch_key";
|
|
};
|
|
};
|
|
}
|
|
#+end_src
|
|
|
|
Okay so I updated my NixOS from 23.05 to 23.11 today and Meilisearch failed to start because the DB is incompatible between versions. You need a running instance to =curl= the dump endpoint, update, and then reimport. I decided to blow away the DB instead and reindex:
|
|
|
|
- =rm /var/lib/meilisearch/*=
|
|
- =systemctl start meilisearch=, look in the journal to see what the new master key is
|
|
- =pleroma_ctl search.meilisearch show-keys $THE_ADMIN_KEY= and take the "use for all operations" key and stick it in =/srv/akkoma/meilisearch_key= or whatever
|
|
- =systemctl restart akkoma-config && systemctl restart akkoma= to bring the secret in to the configuration
|
|
- =pleroma_ctl search.meilisearch index=
|
|
|
|
** NEXT Akkoma Moderation Rules
|
|
|
|
the new [[https://docs.akkoma.dev/stable-docs/configuration/mrf/][Message Rewrite Facility]] x [[https://fediblock.neocities.org/][fediblock]] dropped. I don't see a lot of this stuff and part of me thinks that having it boosted in to my TWKN or even home timeline is a great signal that I should be unfollowing whoever is bringing the filth in to my instance, but also I want to respect my own sanity.
|
|
|
|
#+begin_src nix :tangle ~/arroyo-nix/nixos/akkoma-mrf.nix :noweb yes
|
|
{ pkgs, ... }:
|
|
|
|
let ecf = pkgs.formats.elixirConf {};
|
|
in
|
|
{
|
|
services.akkoma.config = {
|
|
":pleroma".":instance" = {
|
|
quarantined_instances = [
|
|
<<quarantined_instances()>>
|
|
];
|
|
};
|
|
|
|
":pleroma".":mrf".policies = ecf.lib.mkRaw "Pleroma.Web.ActivityPub.MRF.SimplePolicy";
|
|
":pleroma".":mrf_simple".reject = [
|
|
<<reject_instances()>>
|
|
];
|
|
};
|
|
}
|
|
#+end_src
|
|
|
|
These two functions generate those from a table I don't export.
|
|
|
|
#+NAME: quarantined_instances
|
|
#+begin_src emacs-lisp :noweb-ref quarantined_instances :var tbl=blocked-domains
|
|
(->> tbl
|
|
(-map #'first)
|
|
(--map (format "\"%s\"" it))
|
|
(s-join "\n"))
|
|
#+end_src
|
|
|
|
This elixirConf stuff, and the declarative config model, is pretty slick. [[file:~/Code/nixpkgs/pkgs/pkgs-lib/formats.nix::{ lib, pkgs }:][<nixpkgs/pkgs/pkgs-lib/formats.nix>]]
|
|
|
|
#+NAME: reject_instances
|
|
#+begin_src emacs-lisp :noweb-ref reject_instances :var tbl=blocked-domains
|
|
(->> tbl
|
|
(--map (format "(ecf.lib.mkTuple [\"%s\" \"%s\"])" (first it) (second it)))
|
|
(s-join "\n"))
|
|
#+end_src
|
|
|
|
*** Rules aren't exported for personal safety :noexport:
|
|
|
|
#+NAME: blocked-domains
|
|
| domain | reason |
|
|
|-------------------------+-------------------|
|
|
| poa.st | ugh |
|
|
| bae.st | ughh |
|
|
| coon.town | ughhh |
|
|
| kiwifarms.cc | ughhh |
|
|
| chungus.cc | unmoderated |
|
|
| cawfee.club | unmoderated |
|
|
| freespeechextremist.com | ugh |
|
|
| gab.com | ughhh |
|
|
| truthsocial.com | lol |
|
|
| 80percent.social | ugh + spam |
|
|
| rakket.app | ughh |
|
|
| 13bells.com | religious weirdos |
|
|
| pieville.net | nazis |
|
|
| yggdrasil.social | |
|
|
| glindr.org | transphobia |
|
|
| leafposter.club | racism |
|
|
| masochi.st | awful TWKN |
|
|
| pedo.school | yanno |
|
|
|