atagen

malice

public
Star0
Watch0
b39ecae834a0cd6670068d28a7b4afd0498f7964
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git
nix/module.nixNix157 lines4.7 KB
1
{ self }:
2
{
3
  config,
4
  lib,
5
  pkgs,
6
  ...
7
}:
8
let
9
  system = pkgs.stdenv.hostPlatform.system;
10
  hostedCfg = config.services.malice;
11
  deviceCfg = config.programs.malice;
12
  inherit (lib)
13
    mkEnableOption
14
    mkIf
15
    mkMerge
16
    mkOption
17
    types
18
    ;
19
  deviceUrl = "http://127.0.0.1:${toString deviceCfg.port}/";
20
  browserCommand =
21
    if lib.hasInfix "%u" deviceCfg.browser then
22
      lib.replaceStrings [ "%u" ] [ deviceUrl ] deviceCfg.browser
23
    else
24
      "${deviceCfg.browser} ${deviceUrl}";
25
  malicOpen = pkgs.writeShellScriptBin "malice-open" ''
26
    exec ${browserCommand}
27
  '';
28
  malicDesktopItem = pkgs.makeDesktopItem {
29
    name = "malice";
30
    desktopName = "Malice";
31
    comment = "Open the on-device Malice Matrix client";
32
    exec = "${malicOpen}/bin/malice-open";
33
    terminal = false;
34
    categories = [
35
      "Network"
36
      "InstantMessaging"
37
    ];
38
  };
39
in
40
{
41
  options.services.malice = {
42
    enable = mkEnableOption "Malice hosted Matrix client";
43
44
    package = mkOption {
45
      type = types.package;
46
      default = self.packages.${system}.default;
47
      description = "Malice server and bundled Svelte frontend.";
48
    };
49
50
    listenAddress = mkOption {
51
      type = types.str;
52
      default = "127.0.0.1";
53
      description = "Address to bind. Keep behind an HTTPS reverse proxy for secure browser cookies.";
54
    };
55
56
    port = mkOption {
57
      type = types.port;
58
      default = 3000;
59
      description = "HTTP listener port.";
60
    };
61
62
    openFirewall = mkOption {
63
      type = types.bool;
64
      default = false;
65
      description = "Open the HTTP listener port in the firewall.";
66
    };
67
  };
68
69
  options.programs.malice = {
70
    enable = mkEnableOption "Malice on-device Matrix client";
71
72
    package = mkOption {
73
      type = types.package;
74
      default = self.packages.${system}.local;
75
      description = "The malice-static binary wrapped for the on-device build (MALICE_WEB_DIR set).";
76
    };
77
78
    port = mkOption {
79
      type = types.port;
80
      default = 38465;
81
      description = ''
82
        Loopback port the socket-activated server listens on. This port is
83
        part of the browser origin malice runs at, so changing it hides
84
        accounts logged in at the old origin (their data is still on disk,
85
        just unreachable at the new URL). Everyone logged into this machine
86
        at once shares this one port; per-user ports are not supported yet.
87
      '';
88
    };
89
90
    browser = mkOption {
91
      type = types.str;
92
      default = "xdg-open %u";
93
      description = ''
94
        Command the desktop entry runs to open malice. `%u` (desktop-entry
95
        convention) is replaced with malice's URL; if the command has no
96
        `%u`, the URL is appended after a space instead. E.g. "chromium
97
        --app=%u" to open malice in an app window instead of a browser tab.
98
      '';
99
    };
100
  };
101
102
  config = mkMerge [
103
    (mkIf hostedCfg.enable {
104
      systemd.services.malice = {
105
        description = "Malice hosted Matrix client";
106
        wantedBy = [ "multi-user.target" ];
107
        after = [ "network-online.target" ];
108
        wants = [ "network-online.target" ];
109
        environment = {
110
          MALICE_BIND = "${hostedCfg.listenAddress}:${toString hostedCfg.port}";
111
          MALICE_DATA_DIR = "/var/lib/malice";
112
          MALICE_WEB_DIR = "${self.packages.${system}.web}/share/malice";
113
          MALICE_SESSION_COOKIE_SECURE = "true";
114
        };
115
        serviceConfig = {
116
          ExecStart = lib.getExe' hostedCfg.package "malice";
117
          DynamicUser = true;
118
          StateDirectory = "malice";
119
          StateDirectoryMode = "0700";
120
          UMask = "0077";
121
          Restart = "on-failure";
122
          RestartSec = 5;
123
          NoNewPrivileges = true;
124
          ProtectSystem = "strict";
125
          ProtectHome = true;
126
          PrivateTmp = true;
127
          RestrictAddressFamilies = [
128
            "AF_INET"
129
            "AF_INET6"
130
            "AF_UNIX"
131
          ];
132
        };
133
      };
134
      networking.firewall.allowedTCPPorts = lib.optionals hostedCfg.openFirewall [ hostedCfg.port ];
135
    })
136
    (mkIf deviceCfg.enable {
137
      systemd.user.sockets.malice = {
138
        description = "Malice on-device Matrix client socket";
139
        wantedBy = [ "sockets.target" ];
140
        socketConfig.ListenStream = "127.0.0.1:${toString deviceCfg.port}";
141
      };
142
      systemd.user.services.malice = {
143
        description = "Malice on-device Matrix client";
144
        environment.MALICE_IDLE_EXIT_SECS = "300";
145
        serviceConfig = {
146
          ExecStart = lib.getExe' deviceCfg.package "malice-static";
147
        };
148
      };
149
      environment.systemPackages = [
150
        malicOpen
151
      ]
152
      ++ (
153
        if ((builtins.stringLength deviceCfg.browser) != 0) then (lib.singleton malicDesktopItem) else [ ]
154
      );
155
    })
156
  ];
157
}