1 { config, lib, pkgs, ... }:
4 cfg = config.services.archisteamfarm;
6 format = pkgs.formats.json { };
8 configFile = format.generate "ASF.json" (cfg.settings // {
9 # we disable it because ASF cannot update itself anyways
10 # and nixos takes care of restarting the service
11 # is in theory not needed as this is already the default for default builds
14 } // lib.optionalAttrs (cfg.ipcPasswordFile != null) {
15 IPCPassword = "#ipcPassword#";
18 ipc-config = format.generate "IPC.config" cfg.ipcSettings;
21 format.generate "${n}.json" (c.settings // {
22 SteamLogin = if c.username == "" then n else c.username;
24 } // lib.optionalAttrs (c.passwordFile != null) {
25 SteamPassword = c.passwordFile;
26 # sets the password format to file (https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Security#file)
31 options.services.archisteamfarm = {
32 enable = lib.mkOption {
33 type = lib.types.bool;
35 If enabled, starts the ArchisSteamFarm service.
36 For configuring the SteamGuard token you will need to use the web-ui, which is enabled by default over on 127.0.0.1:1242.
37 You cannot configure ASF in any way outside of nix, since all the config files get wiped on restart and replaced with the programnatically set ones by nix.
42 web-ui = lib.mkOption {
43 type = lib.types.submodule {
45 enable = lib.mkEnableOption "" // {
46 description = "Whether to start the web-ui. This is the preferred way of configuring things such as the steam guard token.";
49 package = lib.mkPackageOption pkgs [ "ArchiSteamFarm" "ui" ] {
52 Contents must be in lib/dist
64 description = "The Web-UI hosted on 127.0.0.1:1242.";
67 package = lib.mkPackageOption pkgs "ArchiSteamFarm" {
70 Should always be the latest version, for security reasons,
71 since this module uses very new features and to not get out of sync with the Steam API.
76 dataDir = lib.mkOption {
77 type = lib.types.path;
78 default = "/var/lib/archisteamfarm";
80 The ASF home directory used to store all data.
81 If left as the default value this directory will automatically be created before the ASF server starts, otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership and permissions.'';
84 settings = lib.mkOption {
87 The ASF.json file, all the options are documented [here](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Configuration#global-config).
88 Do note that `AutoRestart` and `UpdateChannel` is always to `false` respectively `0` because NixOS takes care of updating everything.
89 `Headless` is also always set to `true` because there is no way to provide inputs via a systemd service.
90 You should try to keep ASF up to date since upstream does not provide support for anything but the latest version and you're exposing yourself to all kinds of issues - as is outlined [here](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Configuration#updateperiod).
98 ipcPasswordFile = lib.mkOption {
99 type = with lib.types; nullOr path;
101 description = "Path to a file containing the password. The file must be readable by the `archisteamfarm` user/group.";
104 ipcSettings = lib.mkOption {
107 Settings to write to IPC.config.
108 All options can be found [here](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/IPC#custom-configuration).
114 Url = "http://*:1242";
122 bots = lib.mkOption {
123 type = lib.types.attrsOf (lib.types.submodule {
125 username = lib.mkOption {
126 type = lib.types.str;
127 description = "Name of the user to log in. Default is attribute name.";
130 passwordFile = lib.mkOption {
131 type = with lib.types; nullOr path;
134 Path to a file containing the password. The file must be readable by the `archisteamfarm` user/group.
135 Omit or set to null to provide the password a different way, such as through the web-ui.
138 enabled = lib.mkOption {
139 type = lib.types.bool;
141 description = "Whether to enable the bot on startup.";
143 settings = lib.mkOption {
144 type = lib.types.attrs;
146 Additional settings that are documented [here](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Configuration#bot-config).
153 Bots name and configuration.
158 passwordFile = "/var/lib/archisteamfarm/secrets/password";
159 settings = { SteamParentalCode = "1234"; };
166 config = lib.mkIf cfg.enable {
167 # TODO: drop with 24.11
168 services.archisteamfarm.dataDir = lib.mkIf (lib.versionAtLeast config.system.stateVersion "24.05") (lib.mkDefault "/var/lib/asf");
171 users.archisteamfarm = {
174 group = "archisteamfarm";
175 description = "Archis-Steam-Farm service user";
177 groups.archisteamfarm = { };
182 description = "Archis-Steam-Farm Service";
183 after = [ "network.target" ];
184 wantedBy = [ "multi-user.target" ];
186 serviceConfig = lib.mkMerge [
187 (lib.mkIf (lib.hasPrefix "/var/lib/" cfg.dataDir) {
188 StateDirectory = lib.last (lib.splitString "/" cfg.dataDir);
189 StateDirectoryMode = "700";
192 User = "archisteamfarm";
193 Group = "archisteamfarm";
194 WorkingDirectory = cfg.dataDir;
196 ExecStart = "${lib.getExe cfg.package} --no-restart --service --system-required --path ${cfg.dataDir}";
199 # copied from the default systemd service at
200 # https://github.com/JustArchiNET/ArchiSteamFarm/blob/main/ArchiSteamFarm/overlay/variant-base/linux/ArchiSteamFarm%40.service
201 CapabilityBoundingSet = "";
202 DevicePolicy = "closed";
203 LockPersonality = true;
204 NoNewPrivileges = true;
205 PrivateDevices = true;
207 PrivateMounts = true;
208 PrivateTmp = true; # instead of rw /tmp
212 ProtectControlGroups = true;
214 ProtectHostname = true;
215 ProtectKernelLogs = true;
216 ProtectKernelModules = true;
217 ProtectKernelTunables = true;
218 ProtectProc = "invisible";
219 ProtectSystem = "strict";
221 RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK AF_UNIX";
222 RestrictNamespaces = true;
223 RestrictRealtime = true;
224 RestrictSUIDSGID = true;
225 SecureBits = "noroot-locked";
226 SystemCallArchitectures = "native";
227 SystemCallFilter = [ "@system-service" "~@privileged" ];
234 createBotsScript = pkgs.runCommandLocal "ASF-bots" { } ''
236 # clean potential removed bots
238 for i in ${lib.concatStringsSep " " (map (x: "${lib.getName x},${x}") (lib.mapAttrsToList mkBot cfg.bots))}; do IFS=",";
243 replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
248 cp --no-preserve=mode ${configFile} config/ASF.json
250 ${lib.optionalString (cfg.ipcPasswordFile != null) ''
251 ${replaceSecretBin} '#ipcPassword#' '${cfg.ipcPasswordFile}' config/ASF.json
254 ${lib.optionalString (cfg.ipcSettings != {}) ''
255 ln -fs ${ipc-config} config/IPC.config
258 ${lib.optionalString (cfg.bots != {}) ''
259 ln -fs ${createBotsScript}/* config/
263 ${lib.optionalString cfg.web-ui.enable ''
264 ln -s ${cfg.web-ui.package}/ www
272 buildDocsInSandbox = false;
273 maintainers = with lib.maintainers; [ SuperSandro2000 ];