1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.soft-serve;
9 configFile = format.generate "config.yaml" cfg.settings;
10 format = pkgs.formats.yaml { };
11 docUrl = "https://charm.sh/blog/self-hosted-soft-serve/";
12 stateDir = "/var/lib/soft-serve";
13in
14{
15 options = {
16 services.soft-serve = {
17 enable = lib.mkEnableOption "soft-serve";
18
19 package = lib.mkPackageOption pkgs "soft-serve" { };
20
21 settings = lib.mkOption {
22 type = format.type;
23 default = { };
24 description = ''
25 The contents of the configuration file for soft-serve.
26
27 See <${docUrl}>.
28 '';
29 example = lib.literalExpression ''
30 {
31 name = "dadada's repos";
32 log_format = "text";
33 ssh = {
34 listen_addr = ":23231";
35 public_url = "ssh://localhost:23231";
36 max_timeout = 30;
37 idle_timeout = 120;
38 };
39 stats.listen_addr = ":23233";
40 }
41 '';
42 };
43 };
44 };
45
46 config = lib.mkIf cfg.enable {
47
48 systemd.tmpfiles.rules = [
49 # The config file has to be inside the state dir
50 "L+ ${stateDir}/config.yaml - - - - ${configFile}"
51 ];
52
53 systemd.services.soft-serve = {
54 description = "Soft Serve git server";
55 documentation = [ docUrl ];
56 requires = [ "network-online.target" ];
57 after = [ "network-online.target" ];
58 wantedBy = [ "multi-user.target" ];
59
60 environment.SOFT_SERVE_DATA_PATH = stateDir;
61
62 restartTriggers = [ configFile ];
63
64 serviceConfig = {
65 Type = "simple";
66 DynamicUser = true;
67 Restart = "always";
68 ExecStart = "${lib.getExe cfg.package} serve";
69 StateDirectory = "soft-serve";
70 WorkingDirectory = stateDir;
71 RuntimeDirectory = "soft-serve";
72 RuntimeDirectoryMode = "0750";
73 ProcSubset = "pid";
74 ProtectProc = "invisible";
75 UMask = "0027";
76 CapabilityBoundingSet = "";
77 ProtectHome = true;
78 PrivateDevices = true;
79 PrivateUsers = true;
80 ProtectHostname = true;
81 ProtectClock = true;
82 ProtectKernelTunables = true;
83 ProtectKernelModules = true;
84 ProtectKernelLogs = true;
85 ProtectControlGroups = true;
86 RestrictAddressFamilies = [
87 "AF_UNIX"
88 "AF_INET"
89 "AF_INET6"
90 ];
91 RestrictNamespaces = true;
92 LockPersonality = true;
93 MemoryDenyWriteExecute = true;
94 RestrictRealtime = true;
95 RemoveIPC = true;
96 PrivateMounts = true;
97 SystemCallArchitectures = "native";
98 SystemCallFilter = [
99 "@system-service"
100 "~@cpu-emulation @debug @keyring @module @mount @obsolete @privileged @raw-io @reboot @setuid @swap"
101 ];
102 };
103 };
104 };
105
106 meta.maintainers = [ lib.maintainers.dadada ];
107}