{config, pkgs, inputs, system, ...}: let unstable = import inputs.nixpkgs-unstable { inherit system; overlays = [ (self: super: rec { beamPackages = super.beam.packagesWith super.erlang_nox; elixir = beamPackages.elixir_1_13; }) ]; }; staticDir = "/var/lib/akkoma/static"; inherit ((pkgs.formats.elixirConf {}).lib) mkAtom mkTuple; in { sops.secrets = { "akkoma/keyBase" = {}; "akkoma/signingSalt" = {}; "akkoma/jokenDefaultSigner" = {}; }; containers.pleroma = { autoStart = true; privateNetwork = true; hostAddress = "192.168.42.30"; localAddress = "192.168.42.31"; hostAddress6 = "fd00::42:30"; localAddress6 = "fd00::42:31"; bindMounts."/sops" = { hostPath = "/run/secrets/akkoma"; isReadOnly = true; }; config = {pkgs, config, ...}: { # generating the manual will fail when mixing nixos channels, # so disable it here or this won't build at all. documentation.enable = false; imports = [ ("${inputs.nixpkgs-unstable}/nixos/modules/services/web-apps/akkoma.nix") ]; # the pleroma_ctl wrapper uses pkgs.elixir, which without this is not the same # as akkoma's elixir nixpkgs.overlays = [ (self: super: { elixir = unstable.beamPackages.elixir_1_13; }) ]; system.stateVersion = "22.11"; services.akkoma = { enable = true; package = unstable.akkoma; frontends = { primary = { package = unstable.akkoma-frontends.akkoma-fe; name = "pleroma-fe"; ref = "stable"; }; admin = { package = unstable.akkoma-frontends.admin-fe; name = "admin-fe"; ref = "stable"; }; }; extraStatic."static/terms-of-service.html" = pkgs.writeText "terms-of-service-html" ''

toki!

lawa kepeken pi tomo akkoma ni li lili: sina li mi la, sina ken kepeken e tomo ni.

''; config = { ":pleroma"."Pleroma.Web.Endpoint" = { "url" = { host = "pleroma.stuebinm.eu"; scheme = "https"; port = 443; }; "http" = { ip = "::"; port = 4000; }; secret_key_base._secret = "/sops/keyBase"; signing_salt._secret = "/sops/signingSalt"; }; ":joken".":default_signer"._secret = "/sops/jokenDefaultSigner"; ":pleroma" = { ":instance" = { name = "Pleroma"; limit = 5000; registrations_open = false; federating = true; healthcheck = true; allow_relay = true; description = "a test instance"; email = "dings@dings"; }; ":media_proxy" = { enabled = false; redirect_on_failure = true; }; ":mrf_simple" = { reject = [ (mkTuple ["mastodon.social" "spam"]) (mkTuple ["mstdn.social" "scraping for language model"]) (mkTuple ["uden.ai" "lol"]) ]; }; ":mrf".policies = map mkAtom [ "Pleroma.Web.ActivityPub.MRF.SimplePolicy" ]; "Pleroma.Upload" = { filters = map mkAtom [ "Pleroma.Upload.Filter.Exiftool" "Pleroma.Upload.Filter.AnonymizeFilename" "Pleroma.Upload.Filter.Dedupe" ]; }; # "Pleroma.Uploaders.Local".uploads = "/var/lib/akkoma/uploads"; "Pleroma.Repo" = { adapter = "Ecto.Adapters.Postgres"; username = "pleroma"; database = "pleroma"; socket_dir = "/run/postgresql"; pool_size = 10; # prepare = ":named"; show_sensitive_data_on_connection_error = true; parameters = { plan_cache_mode = "force_custom_plan"; }; }; ":database".run_enabled = false; ":configurable_from_database" = false; ":instance".static_dir = "/var/lib/akkoma/static"; }; }; }; systemd.services.akkoma = { path = [ pkgs.exiftool ]; bindsTo = [ "akkoma-static.service" ]; after = [ "akkoma-static.service" ]; }; # symlink the parts of the static dir that are inside the nix store, # so I can still have imperatively defined emojis etc. # (for some reason the module doesn't do that) systemd.services.akkoma-static = { description = "Akkoma static dir wrangling"; unitConfig.PropagatesReloadTo = [ "akkoma.service" ]; path = [ pkgs.coreutils ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = with pkgs.lib; '' ${concatStringsSep "\n" (mapAttrsToList (key: val: '' mkdir -p ${staticDir}/frontends/${escapeShellArg val.name}/ ln -sfT ${escapeShellArg val.package} ${staticDir}/frontends/${escapeShellArg val.name}/${escapeShellArg val.ref} '') config.services.akkoma.frontends)} ${optionalString (config.services.akkoma.extraStatic != null) (concatStringsSep "\n" (mapAttrsToList (key: val: '' mkdir -p "${staticDir}/$(dirname ${escapeShellArg key})" ln -sfT ${escapeShellArg val} ${staticDir}/${escapeShellArg key} '') config.services.akkoma.extraStatic))} ''; }; services.postgresql = { enable = true; package = pkgs.postgresql_12; ensureDatabases = [ "pleroma" ]; ensureUsers = [ { name = "pleroma"; ensurePermissions."DATABASE pleroma" = "ALL PRIVILEGES"; } ]; # give pleroma access. must be done with lib.mkForce, for some reason authentication = pkgs.lib.mkForce '' # Generated file; do not edit! local all all trust host pleroma akkoma ::1/128 trust ''; # this is basically legacy. even if I ever reset the database, # the initDb option of the akkoma module probably does about this. initialScript = pkgs.writeScript "postgres-pleroma-initial" '' CREATE USER pleroma; CREATE DATABASE pleroma OWNER pleroma; \c pleroma; --Extensions made by ecto.migrate that need superuser access CREATE EXTENSION IF NOT EXISTS citext; CREATE EXTENSION IF NOT EXISTS pg_trgm; CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; ''; }; networking.firewall.allowedTCPPorts = [ 4000 ]; environment.etc."resolv.conf".text = "nameserver 1.1.1.1"; }; }; # give the container access to the external internet (necessary for # fetching content from other instances). Doesn't appear to work with # IPv6, though ... networking.nat = { enable = true; internalInterfaces = [ "ve-pleroma" ]; externalInterface = "ens3"; }; services.nginx.virtualHosts."pleroma.stuebinm.eu" = { forceSSL = true; enableACME = true; locations."/" = { proxyPass = "http://[${config.containers.pleroma.localAddress6}]:4000"; proxyWebsockets = true; # these headers are in the example config in the NixOS manual. # take some time to figure out what they all do, and if these # are necessary extraConfig = '' add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always; add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always; if ($request_method = OPTIONS) { return 204; } add_header X-XSS-Protection "1; mode=block"; add_header X-Permitted-Cross-Domain-Policies none; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header Referrer-Policy same-origin; add_header X-Download-Options noopen; client_max_body_size 16m; ''; }; }; }