Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions nix/hosts.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ in
cfg.modules.darwin.user-primary
cfg.modules.darwin.agent-browser-shared
cfg.modules.darwin.rosetta-builder
cfg.modules.darwin.slskd
];
};
kikir = {
Expand Down Expand Up @@ -133,6 +134,7 @@ in
cfg.modules.nixos.xremap
cfg.modules.nixos.steam
cfg.modules.nixos.prismlauncher
cfg.modules.nixos.slskd
cfg.modules.nixos.gtnh-server
cfg.modules.nixos.gtnh-backups
(
Expand Down
152 changes: 152 additions & 0 deletions nix/modules/slskd.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
{ config, ... }:
let
cfg = config.flake;
inherit (cfg.paths) root;
sopsFile = root + "/sops/slskd.yaml";
in
{
config.flake.modules = {
darwin.slskd =
{
config,
pkgs,
primaryUser,
...
}:
let
version = "0.26.0";
stateDir = "${config.users.users.${primaryUser.name}.home}/Library/Application Support/slskd";
directories = {
downloads = "${stateDir}/downloads";
incomplete = "${stateDir}/incomplete";
};
package = pkgs.stdenvNoCC.mkDerivation {
pname = "slskd";
inherit version;
src = pkgs.fetchzip {
url = "https://github.com/slskd/slskd/releases/download/${version}/slskd-${version}-osx-arm64.zip";
hash = "sha256-vdoJy2ZpElYJA43mNZfExNK4Mpnw8ApVxVDzPq1s97I=";
stripRoot = false;
};
installPhase = ''
runHook preInstall
mkdir -p "$out/libexec/slskd"
cp -R . "$out/libexec/slskd/"
chmod +x "$out/libexec/slskd/slskd"
runHook postInstall
'';
meta = {
description = "Modern client-server application for the Soulseek file-sharing network";
homepage = "https://github.com/slskd/slskd";
license = pkgs.lib.licenses.agpl3Only;
platforms = [ "aarch64-darwin" ];
};
};
settings = (pkgs.formats.yaml { }).generate "slskd.yml" {
inherit directories;
flags.no_version_check = true;
shares.directories = [ directories.downloads ];
};
in
{
launchd.user.agents.slskd = {
serviceConfig = {
ProgramArguments = [
"${pkgs.writeShellScript "slskd" ''
set -euo pipefail
umask 0077
mkdir -p ${pkgs.lib.escapeShellArg directories.incomplete} ${pkgs.lib.escapeShellArg directories.downloads}
export SLSKD_SLSK_USERNAME="$(<${
config.sops.secrets."slskd/soulseek-username".path
})-${config.networking.hostName}"
export SLSKD_SLSK_PASSWORD="$(<${config.sops.secrets."slskd/soulseek-password".path})"
export SLSKD_USERNAME="$(<${config.sops.secrets."slskd/web-username".path})"
export SLSKD_PASSWORD="$(<${config.sops.secrets."slskd/web-password".path})"
cd ${package}/libexec/slskd
exec ${package}/libexec/slskd/slskd \
--app-dir ${pkgs.lib.escapeShellArg stateDir} \
--config ${settings}
''}"
];
RunAtLoad = true;
KeepAlive.SuccessfulExit = false;
# Change the plist when credentials change so launchd restarts slskd with the new values.
EnvironmentVariables.SLSKD_SECRETS_HASH =
config.sops.secrets."slskd/soulseek-username".sopsFileHash;
};
};

sops.secrets =
let
mkSecret = _: {
inherit sopsFile;
owner = primaryUser.name;
};
in
pkgs.lib.genAttrs [
"slskd/soulseek-username"
"slskd/soulseek-password"
"slskd/web-username"
"slskd/web-password"
] mkSecret;
};

nixos.slskd =
{ config, lib, ... }:
let
directories = {
downloads = "/var/lib/slskd/downloads";
incomplete = "/var/lib/slskd/incomplete";
};
in
{
services.slskd = {
enable = true;
# The default group is slskd. Use users so the primary user and MusicGrabber can access completed files.
group = "users";
environmentFile = config.sops.templates."slskd.env".path;
openFirewall = true;
settings = {
inherit directories;
shares.directories = [ directories.downloads ];
};
};

sops = {
secrets = {
"slskd/soulseek-username".sopsFile = sopsFile;
"slskd/soulseek-password".sopsFile = sopsFile;
"slskd/web-username".sopsFile = sopsFile;
"slskd/web-password".sopsFile = sopsFile;
};
templates."slskd.env" = {
owner = "slskd";
# Restart slskd after credential changes so it reloads the rendered environment file.
restartUnits = [ "slskd.service" ];
content = ''
SLSKD_SLSK_USERNAME=${
config.sops.placeholder."slskd/soulseek-username"
}-${config.networking.hostName}
SLSKD_SLSK_PASSWORD=${config.sops.placeholder."slskd/soulseek-password"}
SLSKD_USERNAME=${config.sops.placeholder."slskd/web-username"}
SLSKD_PASSWORD=${config.sops.placeholder."slskd/web-password"}
'';
};
};

systemd = {
services.slskd.serviceConfig = {
# Nixpkgs makes every shared directory read-only, but this directory also receives completed downloads.
ReadOnlyPaths = lib.mkForce [ ];
StateDirectoryMode = "0750";
UMask = "0007";
WorkingDirectory = "${config.services.slskd.package}/lib/slskd";
};
tmpfiles.rules = [
"d ${directories.incomplete} 0770 slskd users -"
"d ${directories.downloads} 0770 slskd users -"
];
};
};
};
}
20 changes: 20 additions & 0 deletions sops/slskd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
slskd:
soulseek-username: ENC[AES256_GCM,data:feMMR/aqs/EnAkKPPUuDGrM69UE=,iv:qwI7hMw//4YhfMQhhnWCMA2+Ak2NweDn5D1foVfWc4M=,tag:YcvfRSgo1ohnhJNBCB4Psw==,type:str]
soulseek-password: ENC[AES256_GCM,data:uVBxu5dsn/QsIC38oBCTLabtMiRs0/dh2iSAcxPdEB2O,iv:6/Zm3JMoAUqljezcEii3slPD+nbfsyrNj2mQJDjF2u4=,tag:0Pg/mjAQf1FKcQNEQg8IVQ==,type:str]
web-username: ENC[AES256_GCM,data:IaDZYxQ=,iv:FrgChB4v/5ul7BC8GqweGl//LCOc3xrHKMEY2d9pDP8=,tag:vE3+BvgJo42rjrsSuRprGw==,type:str]
web-password: ENC[AES256_GCM,data:vO5DhpcHgDwmolACPiyixZg1Jm10sp0BJHagL2zt8zDpQT1/gn2OSpz7D58=,iv:AD//s4t9PnUQ7xgLDbD1v3UXSf9eHLLzGKmUzN0tD1E=,tag:dOc5qbax30zOwoTC5Lk7uw==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArWUl5dTVDWFNhK003Q3Bu
bWY4RzN6dFUrMzVBSC9LUFF4bW1XQ1BVL1VnClpzZTUxWjhyblUySnFDUVNCRGow
S2lLWGdwdE9adEoyMzZXbE5WSTdqeTQKLS0tIGE1ZUl5UnZvWjNZSGVZQ282dmh0
Nkg5SGJDQ1NIYzJtbnU5dXFWQ3lQZ28KZER5mRr650FJ2fHipze2KmFkvZ7hMJBw
cieRZ8yRR3NFskMFDI+wp8zga+zK/P97sf11zni5YjmsW+8Pr7iUZw==
-----END AGE ENCRYPTED FILE-----
recipient: age12l33pas8eptwjc7ewux3d8snyzfzwz0tn9qg5kw8le79fswmjgjqdjgyy6
lastmodified: "2026-10-25T03:16:54Z"
mac: ENC[AES256_GCM,data:h0mRaqJNO9+osHptEtnQRfK1letH0juPFL8D4pEi42yDMRpiVwUs+dJEzyc+K2z9fYNRlQ1z29gQgOet0hVIJsmGsHeL6Z4THYGCdhIB2hY1+XB0F+ONTj/AQvZXBEzcJkzFwI0UiJtE0ZEDL4QRPIMrusVd8InmODvp3LKhhj8=,iv:g2XiVPR32lpBivGIcO4qTeQxxaZLmoU7xXW1TcDhnqg=,tag:J3CY/y/eeJ7VZfjyN0TeZA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.3
Loading