nixpkgs/nixos/modules/services/home-automation/wyoming/piper.nix
Martin Weinelt 3a4e4f6c3d
nixos/wyoming/piper: relax ProcSubset to all
The onnxruntime library wants to query cpuinfo, which fails when the proc
subset is restricted.

Close: #445723
2025-09-30 00:29:35 +02:00

212 lines
5.7 KiB
Nix

{
config,
lib,
pkgs,
utils,
...
}:
let
cfg = config.services.wyoming.piper;
inherit (lib)
literalExpression
mkOption
mkEnableOption
mkPackageOption
types
;
inherit (builtins)
toString
;
inherit (utils)
escapeSystemdExecArgs
;
in
{
options.services.wyoming.piper = with types; {
package = mkPackageOption pkgs "wyoming-piper" { };
servers = mkOption {
default = { };
description = ''
Attribute set of wyoming-piper instances to spawn.
'';
type = types.attrsOf (
types.submodule (
{ ... }:
{
options = {
enable = mkEnableOption "Wyoming Piper server";
voice = mkOption {
type = str;
example = "en-us-ryan-medium";
description = ''
Name of the voice model to use. See the following website for samples:
https://rhasspy.github.io/piper-samples/
'';
};
uri = mkOption {
type = strMatching "^(tcp|unix)://.*$";
example = "tcp://0.0.0.0:10200";
description = ''
URI to bind the wyoming server to.
'';
};
speaker = mkOption {
type = ints.unsigned;
default = 0;
description = ''
ID of a specific speaker in a multi-speaker model.
'';
apply = toString;
};
noiseScale = mkOption {
type = float;
default = 0.667;
description = ''
Generator noise value.
'';
apply = toString;
};
noiseWidth = mkOption {
type = float;
default = 0.333;
description = ''
Phoneme width noise value.
'';
apply = toString;
};
lengthScale = mkOption {
type = float;
default = 1.0;
description = ''
Phoneme length value.
'';
apply = toString;
};
streaming = mkEnableOption "audio streaming on sentence boundaries" // {
default = true;
};
useCUDA = mkOption {
type = bool;
default = pkgs.config.cudaSupport;
defaultText = literalExpression "pkgs.config.cudaSupport";
description = ''
Whether to accelerate the underlying onnxruntime library with CUDA.
'';
};
extraArgs = mkOption {
type = listOf str;
default = [ ];
description = ''
Extra arguments to pass to the server commandline.
'';
};
};
}
)
);
};
};
config =
let
inherit (lib)
mapAttrs'
mkIf
nameValuePair
;
in
mkIf (cfg.servers != { }) {
systemd.services = mapAttrs' (
server: options:
nameValuePair "wyoming-piper-${server}" {
inherit (options) enable;
description = "Wyoming Piper server instance ${server}";
wants = [
"network-online.target"
];
after = [
"network-online.target"
];
wantedBy = [
"multi-user.target"
];
serviceConfig = {
DynamicUser = true;
User = "wyoming-piper";
StateDirectory = [ "wyoming/piper" ];
# https://github.com/home-assistant/addons/blob/master/piper/rootfs/etc/s6-overlay/s6-rc.d/piper/run
ExecStart = escapeSystemdExecArgs (
[
(lib.getExe cfg.package)
"--data-dir"
"/var/lib/wyoming/piper"
"--uri"
options.uri
"--voice"
options.voice
"--speaker"
options.speaker
"--length-scale"
options.lengthScale
"--noise-scale"
options.noiseScale
"--noise-w-scale"
options.noiseWidth
]
++ lib.optionals options.streaming [
"--streaming"
]
++ lib.optionals options.useCUDA [
"--use-cuda"
]
++ options.extraArgs
);
CapabilityBoundingSet = "";
DeviceAllow = "";
DevicePolicy = "closed";
LockPersonality = true;
MemoryDenyWriteExecute = false; # required for onnxruntime
PrivateDevices = true;
PrivateUsers = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
ProtectProc = "invisible";
ProcSubset = "all"; # for onnxruntime, which queries cpuinfo
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged"
];
UMask = "0077";
};
}
) cfg.servers;
};
}