1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-11-08 11:36:05 +01:00
home-manager/modules/services/radicle.nix
Lorenz Leutgeb 2aaf924e82
radicle: init (#5409)
Co-authored-by: Matthias Beyer <mail@beyermatthias.de>
Co-authored-by: Austin Horstman <khaneliman12@gmail.com>
2025-10-26 15:34:09 -05:00

245 lines
7.1 KiB
Nix

{
config,
lib,
pkgs,
options,
...
}:
let
inherit (lib)
generators
getBin
getExe'
last
mapAttrsToList
mkDefault
mkEnableOption
mkIf
mkOption
mkMerge
mkPackageOption
splitString
;
inherit (lib.types)
attrsOf
nullOr
oneOf
package
path
str
;
cfg = config.services.radicle;
radicleHome = config.home.homeDirectory + "/.radicle";
gitPath = [ "PATH=${getBin pkgs.gitMinimal}/bin" ];
env = attrs: (mapAttrsToList (generators.mkKeyValueDefault { } "=") attrs) ++ gitPath;
in
{
options = {
services.radicle = {
node = {
enable = mkEnableOption "Radicle Node";
package = mkPackageOption pkgs "radicle-node" { };
args = mkOption {
type = str;
description = "Additional command line arguments to pass when executing `radicle-node`.";
default = "";
example = "--force";
};
environment = mkOption {
type = attrsOf (
nullOr (oneOf [
str
path
package
])
);
description = "Environment to set when executing `radicle-node`.";
default = { };
example = {
"RUST_BACKTRACE" = "full";
};
};
lazy = {
enable = mkEnableOption "a proxy service to lazily start and stop Radicle Node on demand";
exitIdleTime = mkOption {
type = str;
description = "The idle time after which no interaction with Radicle Node via the `rad` CLI should be stopped, in a format that {manpage}`systemd-socket-proxyd(8)` understands for its `--exit-idle-time` argument.";
default = "30min";
example = "1h";
};
};
};
};
};
config = mkIf cfg.node.enable {
systemd.user = {
services = {
"radicle-node" =
let
keyFile = name: "${radicleHome}/keys/${name}";
keyPair = name: [
(keyFile name)
(keyFile (name + ".pub"))
];
radicleKeyPair = keyPair "radicle";
in
{
Unit = {
Description = "Radicle Node";
Documentation = [
"https://radicle.xyz/guides"
"man:radicle-node(1)"
];
StopWhenUnneeded = cfg.node.lazy.enable;
ConditionPathExists = radicleKeyPair;
};
Service = mkMerge ([
{
Slice = "session.slice";
ExecStart = "${getExe' cfg.node.package "radicle-node"} ${cfg.node.args}";
Environment = env cfg.node.environment;
KillMode = "process";
Restart = "no";
RestartSec = "2";
RestartSteps = "100";
RestartMaxDelaySec = "1min";
}
{
# Hardening
BindPaths = [
"${radicleHome}/storage"
"${radicleHome}/node"
"${radicleHome}/cobs"
];
BindReadOnlyPaths = [
"${radicleHome}/config.json"
"${radicleHome}/keys"
"-/etc/resolv.conf"
"/run/systemd"
];
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
AmbientCapabilities = "";
CapabilityBoundingSet = "";
NoNewPrivileges = true;
DeviceAllow = ""; # ProtectClock= adds DeviceAllow=char-rtc r
KeyringMode = "private";
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateDevices = true;
PrivateTmp = true;
PrivateUsers = "self";
ProcSubset = "pid";
ProtectClock = true;
ProtectHome = "tmpfs";
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RuntimeDirectoryMode = "0700";
SocketBindDeny = [ "any" ];
SocketBindAllow = map (
addr: "tcp:${last (splitString ":" addr)}"
) config.programs.radicle.settings.node.listen;
StateDirectoryMode = "0750";
UMask = "0067";
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@aio"
"~@chown"
"~@keyring"
"~@memlock"
"~@privileged"
"~@resources"
"~@setuid"
];
}
]);
};
"radicle-node-proxy" = mkIf cfg.node.lazy.enable {
Unit = {
Description = "Radicle Node Proxy";
BindsTo = [
"radicle-node-proxy.socket"
"radicle-node.service"
];
After = [
"radicle-node-proxy.socket"
"radicle-node.service"
];
Documentation = [ "man:systemd-socket-proxyd(8)" ];
};
Service = {
ExecSearchPath = "${pkgs.systemd}/lib/systemd";
ExecStart = "systemd-socket-proxyd --exit-idle-time=${cfg.node.lazy.exitIdleTime} %t/radicle-node/proxy.sock";
PrivateTmp = "yes";
PrivateNetwork = "yes";
RuntimeDirectory = "radicle";
RuntimeDirectoryPreserve = "yes";
};
};
};
sockets = mkIf cfg.node.lazy.enable {
"radicle-node-control" = {
Unit = {
Description = "Radicle Node Control Socket";
Documentation = [ "man:radicle-node(1)" ];
};
Socket = {
Service = "radicle-node-proxy.service";
ListenStream = "%t/radicle-node/control.sock";
RuntimeDirectory = "radicle-node";
RuntimeDirectoryPreserve = "yes";
};
Install.WantedBy = [ "sockets.target" ];
};
"radicle-node-proxy" = {
Unit = {
Description = "Radicle Node Proxy Socket";
Documentation = [ "man:systemd-socket-proxyd(8)" ];
};
Socket = {
Service = "radicle-node.service";
FileDescriptorName = "control";
ListenStream = "%t/radicle-node/proxy.sock";
RuntimeDirectory = "radicle-node";
RuntimeDirectoryPreserve = "yes";
};
Install.WantedBy = [ "sockets.target" ];
};
};
};
programs.radicle.enable = mkDefault true;
home.sessionVariables = mkIf cfg.node.lazy.enable {
RAD_SOCKET = "\${XDG_RUNTIME_DIR:-/run/user/$UID}/radicle-node/control.sock";
};
};
meta.maintainers = with lib.maintainers; [
lorenzleutgeb
matthiasbeyer
];
}