atagen

malice

public
Star0
Watch0
ed3363baaf0183507124a2397eaa37604e1a0fa0
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: nix/module.nix

973f8fbdaf80atagen2026-09-28 09:10 UTC1
{ self }:
973f8fbdaf80atagen2026-09-28 09:10 UTC2
{
973f8fbdaf80atagen2026-09-28 09:10 UTC3
  config,
973f8fbdaf80atagen2026-09-28 09:10 UTC4
  lib,
973f8fbdaf80atagen2026-09-28 09:10 UTC5
  pkgs,
973f8fbdaf80atagen2026-09-28 09:10 UTC6
  ...
973f8fbdaf80atagen2026-09-28 09:10 UTC7
}:
973f8fbdaf80atagen2026-09-28 09:10 UTC8
let
973f8fbdaf80atagen2026-09-28 09:10 UTC9
  system = pkgs.stdenv.hostPlatform.system;
973f8fbdaf80atagen2026-09-28 09:10 UTC10
  hostedCfg = config.services.malice;
973f8fbdaf80atagen2026-09-28 09:10 UTC11
  deviceCfg = config.programs.malice;
973f8fbdaf80atagen2026-09-28 09:10 UTC12
  inherit (lib)
973f8fbdaf80atagen2026-09-28 09:10 UTC13
    mkEnableOption
973f8fbdaf80atagen2026-09-28 09:10 UTC14
    mkIf
973f8fbdaf80atagen2026-09-28 09:10 UTC15
    mkMerge
973f8fbdaf80atagen2026-09-28 09:10 UTC16
    mkOption
973f8fbdaf80atagen2026-09-28 09:10 UTC17
    types
973f8fbdaf80atagen2026-09-28 09:10 UTC18
    ;
973f8fbdaf80atagen2026-09-28 09:10 UTC19
  deviceUrl = "http://127.0.0.1:${toString deviceCfg.port}/";
973f8fbdaf80atagen2026-09-28 09:10 UTC20
  browserCommand =
973f8fbdaf80atagen2026-09-28 09:10 UTC21
    if lib.hasInfix "%u" deviceCfg.browser then
973f8fbdaf80atagen2026-09-28 09:10 UTC22
      lib.replaceStrings [ "%u" ] [ deviceUrl ] deviceCfg.browser
973f8fbdaf80atagen2026-09-28 09:10 UTC23
    else
973f8fbdaf80atagen2026-09-28 09:10 UTC24
      "${deviceCfg.browser} ${deviceUrl}";
973f8fbdaf80atagen2026-09-28 09:10 UTC25
  malicOpen = pkgs.writeShellScriptBin "malice-open" ''
973f8fbdaf80atagen2026-09-28 09:10 UTC26
    exec ${browserCommand}
973f8fbdaf80atagen2026-09-28 09:10 UTC27
  '';
973f8fbdaf80atagen2026-09-28 09:10 UTC28
  malicDesktopItem = pkgs.makeDesktopItem {
973f8fbdaf80atagen2026-09-28 09:10 UTC29
    name = "malice";
973f8fbdaf80atagen2026-09-28 09:10 UTC30
    desktopName = "Malice";
973f8fbdaf80atagen2026-09-28 09:10 UTC31
    comment = "Open the on-device Malice Matrix client";
973f8fbdaf80atagen2026-09-28 09:10 UTC32
    exec = "${malicOpen}/bin/malice-open";
973f8fbdaf80atagen2026-09-28 09:10 UTC33
    terminal = false;
973f8fbdaf80atagen2026-09-28 09:10 UTC34
    categories = [
973f8fbdaf80atagen2026-09-28 09:10 UTC35
      "Network"
973f8fbdaf80atagen2026-09-28 09:10 UTC36
      "InstantMessaging"
973f8fbdaf80atagen2026-09-28 09:10 UTC37
    ];
973f8fbdaf80atagen2026-09-28 09:10 UTC38
  };
973f8fbdaf80atagen2026-09-28 09:10 UTC39
in
973f8fbdaf80atagen2026-09-28 09:10 UTC40
{
973f8fbdaf80atagen2026-09-28 09:10 UTC41
  options.services.malice = {
973f8fbdaf80atagen2026-09-28 09:10 UTC42
    enable = mkEnableOption "Malice hosted Matrix client";
973f8fbdaf80atagen2026-09-28 09:10 UTC43
973f8fbdaf80atagen2026-09-28 09:10 UTC44
    package = mkOption {
973f8fbdaf80atagen2026-09-28 09:10 UTC45
      type = types.package;
973f8fbdaf80atagen2026-09-28 09:10 UTC46
      default = self.packages.${system}.default;
973f8fbdaf80atagen2026-09-28 09:10 UTC47
      description = "Malice server and bundled Svelte frontend.";
973f8fbdaf80atagen2026-09-28 09:10 UTC48
    };
973f8fbdaf80atagen2026-09-28 09:10 UTC49
973f8fbdaf80atagen2026-09-28 09:10 UTC50
    listenAddress = mkOption {
973f8fbdaf80atagen2026-09-28 09:10 UTC51
      type = types.str;
973f8fbdaf80atagen2026-09-28 09:10 UTC52
      default = "127.0.0.1";
973f8fbdaf80atagen2026-09-28 09:10 UTC53
      description = "Address to bind. Keep behind an HTTPS reverse proxy for secure browser cookies.";
973f8fbdaf80atagen2026-09-28 09:10 UTC54
    };
973f8fbdaf80atagen2026-09-28 09:10 UTC55
973f8fbdaf80atagen2026-09-28 09:10 UTC56
    port = mkOption {
973f8fbdaf80atagen2026-09-28 09:10 UTC57
      type = types.port;
973f8fbdaf80atagen2026-09-28 09:10 UTC58
      default = 3000;
973f8fbdaf80atagen2026-09-28 09:10 UTC59
      description = "HTTP listener port.";
973f8fbdaf80atagen2026-09-28 09:10 UTC60
    };
973f8fbdaf80atagen2026-09-28 09:10 UTC61
973f8fbdaf80atagen2026-09-28 09:10 UTC62
    openFirewall = mkOption {
973f8fbdaf80atagen2026-09-28 09:10 UTC63
      type = types.bool;
973f8fbdaf80atagen2026-09-28 09:10 UTC64
      default = false;
973f8fbdaf80atagen2026-09-28 09:10 UTC65
      description = "Open the HTTP listener port in the firewall.";
973f8fbdaf80atagen2026-09-28 09:10 UTC66
    };
973f8fbdaf80atagen2026-09-28 09:10 UTC67
  };
973f8fbdaf80atagen2026-09-28 09:10 UTC68
973f8fbdaf80atagen2026-09-28 09:10 UTC69
  options.programs.malice = {
973f8fbdaf80atagen2026-09-28 09:10 UTC70
    enable = mkEnableOption "Malice on-device Matrix client";
973f8fbdaf80atagen2026-09-28 09:10 UTC71
973f8fbdaf80atagen2026-09-28 09:10 UTC72
    package = mkOption {
973f8fbdaf80atagen2026-09-28 09:10 UTC73
      type = types.package;
973f8fbdaf80atagen2026-09-28 09:10 UTC74
      default = self.packages.${system}.local;
dffd63b58af4atagen2026-09-28 09:10 UTC75
      description = "The malice-static binary wrapped for the on-device build (MALICE_WEB_DIR set).";
973f8fbdaf80atagen2026-09-28 09:10 UTC76
    };
973f8fbdaf80atagen2026-09-28 09:10 UTC77
973f8fbdaf80atagen2026-09-28 09:10 UTC78
    port = mkOption {
973f8fbdaf80atagen2026-09-28 09:10 UTC79
      type = types.port;
973f8fbdaf80atagen2026-09-28 09:10 UTC80
      default = 38465;
973f8fbdaf80atagen2026-09-28 09:10 UTC81
      description = ''
973f8fbdaf80atagen2026-09-28 09:10 UTC82
        Loopback port the socket-activated server listens on. This port is
973f8fbdaf80atagen2026-09-28 09:10 UTC83
        part of the browser origin malice runs at, so changing it hides
973f8fbdaf80atagen2026-09-28 09:10 UTC84
        accounts logged in at the old origin (their data is still on disk,
973f8fbdaf80atagen2026-09-28 09:10 UTC85
        just unreachable at the new URL). Everyone logged into this machine
973f8fbdaf80atagen2026-09-28 09:10 UTC86
        at once shares this one port; per-user ports are not supported yet.
973f8fbdaf80atagen2026-09-28 09:10 UTC87
      '';
973f8fbdaf80atagen2026-09-28 09:10 UTC88
    };
973f8fbdaf80atagen2026-09-28 09:10 UTC89
973f8fbdaf80atagen2026-09-28 09:10 UTC90
    browser = mkOption {
973f8fbdaf80atagen2026-09-28 09:10 UTC91
      type = types.str;
973f8fbdaf80atagen2026-09-28 09:10 UTC92
      default = "xdg-open %u";
973f8fbdaf80atagen2026-09-28 09:10 UTC93
      description = ''
973f8fbdaf80atagen2026-09-28 09:10 UTC94
        Command the desktop entry runs to open malice. `%u` (desktop-entry
973f8fbdaf80atagen2026-09-28 09:10 UTC95
        convention) is replaced with malice's URL; if the command has no
973f8fbdaf80atagen2026-09-28 09:10 UTC96
        `%u`, the URL is appended after a space instead. E.g. "chromium
973f8fbdaf80atagen2026-09-28 09:10 UTC97
        --app=%u" to open malice in an app window instead of a browser tab.
973f8fbdaf80atagen2026-09-28 09:10 UTC98
      '';
973f8fbdaf80atagen2026-09-28 09:10 UTC99
    };
973f8fbdaf80atagen2026-09-28 09:10 UTC100
  };
973f8fbdaf80atagen2026-09-28 09:10 UTC101
973f8fbdaf80atagen2026-09-28 09:10 UTC102
  config = mkMerge [
973f8fbdaf80atagen2026-09-28 09:10 UTC103
    (mkIf hostedCfg.enable {
973f8fbdaf80atagen2026-09-28 09:10 UTC104
      systemd.services.malice = {
973f8fbdaf80atagen2026-09-28 09:10 UTC105
        description = "Malice hosted Matrix client";
973f8fbdaf80atagen2026-09-28 09:10 UTC106
        wantedBy = [ "multi-user.target" ];
973f8fbdaf80atagen2026-09-28 09:10 UTC107
        after = [ "network-online.target" ];
973f8fbdaf80atagen2026-09-28 09:10 UTC108
        wants = [ "network-online.target" ];
973f8fbdaf80atagen2026-09-28 09:10 UTC109
        environment = {
973f8fbdaf80atagen2026-09-28 09:10 UTC110
          MALICE_BIND = "${hostedCfg.listenAddress}:${toString hostedCfg.port}";
973f8fbdaf80atagen2026-09-28 09:10 UTC111
          MALICE_DATA_DIR = "/var/lib/malice";
973f8fbdaf80atagen2026-09-28 09:10 UTC112
          MALICE_WEB_DIR = "${self.packages.${system}.web}/share/malice";
973f8fbdaf80atagen2026-09-28 09:10 UTC113
          MALICE_SESSION_COOKIE_SECURE = "true";
973f8fbdaf80atagen2026-09-28 09:10 UTC114
        };
973f8fbdaf80atagen2026-09-28 09:10 UTC115
        serviceConfig = {
973f8fbdaf80atagen2026-09-28 09:10 UTC116
          ExecStart = lib.getExe' hostedCfg.package "malice";
973f8fbdaf80atagen2026-09-28 09:10 UTC117
          DynamicUser = true;
973f8fbdaf80atagen2026-09-28 09:10 UTC118
          StateDirectory = "malice";
973f8fbdaf80atagen2026-09-28 09:10 UTC119
          StateDirectoryMode = "0700";
973f8fbdaf80atagen2026-09-28 09:10 UTC120
          UMask = "0077";
973f8fbdaf80atagen2026-09-28 09:10 UTC121
          Restart = "on-failure";
973f8fbdaf80atagen2026-09-28 09:10 UTC122
          RestartSec = 5;
973f8fbdaf80atagen2026-09-28 09:10 UTC123
          NoNewPrivileges = true;
973f8fbdaf80atagen2026-09-28 09:10 UTC124
          ProtectSystem = "strict";
973f8fbdaf80atagen2026-09-28 09:10 UTC125
          ProtectHome = true;
973f8fbdaf80atagen2026-09-28 09:10 UTC126
          PrivateTmp = true;
973f8fbdaf80atagen2026-09-28 09:10 UTC127
          RestrictAddressFamilies = [
973f8fbdaf80atagen2026-09-28 09:10 UTC128
            "AF_INET"
973f8fbdaf80atagen2026-09-28 09:10 UTC129
            "AF_INET6"
973f8fbdaf80atagen2026-09-28 09:10 UTC130
            "AF_UNIX"
973f8fbdaf80atagen2026-09-28 09:10 UTC131
          ];
973f8fbdaf80atagen2026-09-28 09:10 UTC132
        };
973f8fbdaf80atagen2026-09-28 09:10 UTC133
      };
973f8fbdaf80atagen2026-09-28 09:10 UTC134
      networking.firewall.allowedTCPPorts = lib.optionals hostedCfg.openFirewall [ hostedCfg.port ];
973f8fbdaf80atagen2026-09-28 09:10 UTC135
    })
973f8fbdaf80atagen2026-09-28 09:10 UTC136
    (mkIf deviceCfg.enable {
973f8fbdaf80atagen2026-09-28 09:10 UTC137
      systemd.user.sockets.malice = {
973f8fbdaf80atagen2026-09-28 09:10 UTC138
        description = "Malice on-device Matrix client socket";
973f8fbdaf80atagen2026-09-28 09:10 UTC139
        wantedBy = [ "sockets.target" ];
973f8fbdaf80atagen2026-09-28 09:10 UTC140
        socketConfig.ListenStream = "127.0.0.1:${toString deviceCfg.port}";
973f8fbdaf80atagen2026-09-28 09:10 UTC141
      };
973f8fbdaf80atagen2026-09-28 09:10 UTC142
      systemd.user.services.malice = {
973f8fbdaf80atagen2026-09-28 09:10 UTC143
        description = "Malice on-device Matrix client";
dffd63b58af4atagen2026-09-28 09:10 UTC144
        environment.MALICE_IDLE_EXIT_SECS = "300";
973f8fbdaf80atagen2026-09-28 09:10 UTC145
        serviceConfig = {
dffd63b58af4atagen2026-09-28 09:10 UTC146
          ExecStart = lib.getExe' deviceCfg.package "malice-static";
973f8fbdaf80atagen2026-09-28 09:10 UTC147
        };
973f8fbdaf80atagen2026-09-28 09:10 UTC148
      };
973f8fbdaf80atagen2026-09-28 09:10 UTC149
      environment.systemPackages = [
973f8fbdaf80atagen2026-09-28 09:10 UTC150
        malicOpen
dffd63b58af4atagen2026-09-28 09:10 UTC151
      ]
dffd63b58af4atagen2026-09-28 09:10 UTC152
      ++ (
dffd63b58af4atagen2026-09-28 09:10 UTC153
        if ((builtins.stringLength deviceCfg.browser) != 0) then (lib.singleton malicDesktopItem) else [ ]
dffd63b58af4atagen2026-09-28 09:10 UTC154
      );
973f8fbdaf80atagen2026-09-28 09:10 UTC155
    })
973f8fbdaf80atagen2026-09-28 09:10 UTC156
  ];
973f8fbdaf80atagen2026-09-28 09:10 UTC157
}