atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
e028a4184d000c9c7b61f60e056db01c53aeea23
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
nix/module.nixNix142 lines4.0 KB
1
self:
2
{
3
  config,
4
  lib,
5
  pkgs,
6
  ...
7
}:
8
let
9
  cfg = config.services.jelqd;
10
  host = if lib.hasInfix ":" cfg.address then "[${cfg.address}]" else cfg.address;
11
in
12
{
13
  options.services.jelqd = {
14
    enable = lib.mkEnableOption "jelqd, a roguelike played entirely over HTTP";
15
16
    package = lib.mkOption {
17
      type = lib.types.package;
18
      default = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
19
      defaultText = lib.literalExpression "jelq.packages.\${pkgs.stdenv.hostPlatform.system}.default";
20
      description = "The jelqd package to run.";
21
    };
22
23
    address = lib.mkOption {
24
      type = lib.types.str;
25
      default = "127.0.0.1";
26
      description = "Address to listen on.";
27
    };
28
29
    port = lib.mkOption {
30
      type = lib.types.port;
31
      default = 6969;
32
      description = "Port to listen on.";
33
    };
34
35
    openFirewall = lib.mkOption {
36
      type = lib.types.bool;
37
      default = false;
38
      description = "Open the listening port in the firewall.";
39
    };
40
41
    dataDir = lib.mkOption {
42
      type = lib.types.path;
43
      default = "/var/lib/jelqd";
44
      description = "Directory holding the sqlite database of finished runs.";
45
    };
46
47
    oracle = lib.mkOption {
48
      type = lib.types.enum [
49
        "rng"
50
        "clm"
51
      ];
52
      default = "rng";
53
      description = "Where the oracle gets its answers.";
54
    };
55
56
    clmUrl = lib.mkOption {
57
      type = lib.types.str;
58
      default = "http://127.0.0.1:8700/v1/systemone";
59
      description = "CLM endpoint used when `oracle` is `clm`.";
60
    };
61
62
    environmentFile = lib.mkOption {
63
      type = lib.types.nullOr lib.types.path;
64
      default = null;
65
      example = "/run/secrets/jelqd.env";
66
      description = ''
67
        Environment file passed to the service, for setting `JELQD_SECRET`.
68
        Without it the run-signing secret is regenerated on every restart
69
        and live runs stop being accepted.
70
      '';
71
    };
72
  };
73
74
  config = lib.mkIf cfg.enable {
75
    users.users.jelqd = {
76
      isSystemUser = true;
77
      group = "jelqd";
78
      home = cfg.dataDir;
79
    };
80
    users.groups.jelqd = { };
81
82
    systemd.tmpfiles.settings.jelqd = lib.mkIf (cfg.dataDir != "/var/lib/jelqd") {
83
      ${cfg.dataDir}.d = {
84
        user = "jelqd";
85
        group = "jelqd";
86
        mode = "0750";
87
      };
88
    };
89
90
    systemd.services.jelqd = {
91
      description = "jelqd roguelike server";
92
      wantedBy = [ "multi-user.target" ];
93
      after = [ "network.target" ];
94
      environment = {
95
        JELQD_ADDR = "${host}:${toString cfg.port}";
96
        JELQD_DB = "${cfg.dataDir}/jelqd.sqlite";
97
        JELQD_ORACLE = cfg.oracle;
98
      }
99
      // lib.optionalAttrs (cfg.oracle == "clm") { JELQD_CLM_URL = cfg.clmUrl; };
100
      serviceConfig = {
101
        ExecStart = lib.getExe cfg.package;
102
        User = "jelqd";
103
        Group = "jelqd";
104
        Restart = "on-failure";
105
        EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
106
        StateDirectory = lib.mkIf (cfg.dataDir == "/var/lib/jelqd") "jelqd";
107
        StateDirectoryMode = "0750";
108
        ReadWritePaths = [ cfg.dataDir ];
109
        CapabilityBoundingSet = "";
110
        LockPersonality = true;
111
        MemoryDenyWriteExecute = true;
112
        NoNewPrivileges = true;
113
        PrivateDevices = true;
114
        PrivateTmp = true;
115
        ProtectClock = true;
116
        ProtectControlGroups = true;
117
        ProtectHome = true;
118
        ProtectHostname = true;
119
        ProtectKernelLogs = true;
120
        ProtectKernelModules = true;
121
        ProtectKernelTunables = true;
122
        ProtectProc = "invisible";
123
        ProtectSystem = "strict";
124
        RestrictAddressFamilies = [
125
          "AF_INET"
126
          "AF_INET6"
127
        ];
128
        RestrictNamespaces = true;
129
        RestrictRealtime = true;
130
        RestrictSUIDSGID = true;
131
        SystemCallArchitectures = "native";
132
        SystemCallFilter = [
133
          "@system-service"
134
          "~@privileged"
135
        ];
136
        UMask = "0077";
137
      };
138
    };
139
140
    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];
141
  };
142
}