# Home Manager module: `services.maestro`. # # Maestro owns the user's interactive shells and tmux sessions, so the daemon # is a *user* service, not a system one — same trust boundary as the tmux # server it wraps. It serves the browser UI too; there is no second unit. { self, ... }: { flake.homeManagerModules.default = { config, lib, pkgs, ... }: let cfg = config.services.maestro; tomlFormat = pkgs.formats.toml { }; # A user unit inherits almost no PATH. Give the daemon the profile dirs a # login shell would have, so the sessions it spawns can find the user's # own tools. profilePath = lib.concatStringsSep ":" [ "%h/.local/bin" "%h/.nix-profile/bin" "/etc/profiles/per-user/%u/bin" "/run/current-system/sw/bin" ]; in { imports = [ (lib.mkRemovedOptionModule [ "services" "maestro" "web" "enable" ] '' The daemon serves the browser UI off its own listener now, so there is no second unit to enable. Point a browser at `services.maestro.settings.daemon.bind_addr` instead. '') (lib.mkRemovedOptionModule [ "services" "maestro" "web" "package" ] '' The `maestro-web` package is gone; the SPA is embedded in the `maestro` binary under its default-on `web` cargo feature. '') ]; options.services.maestro = { enable = lib.mkEnableOption "Maestro — installs the CLI/TUI and, unless disabled, runs the daemon"; package = lib.mkOption { type = lib.types.package; default = self.packages.${pkgs.stdenv.hostPlatform.system}.maestro; defaultText = lib.literalExpression "maestro.packages.\${system}.maestro"; description = "The unified `maestro` binary (CLI + daemon + TUI)."; }; daemon.enable = lib.mkOption { type = lib.types.bool; default = true; description = "Run `maestro daemon` as a systemd user service."; }; settings = lib.mkOption { type = tomlFormat.type; default = { }; example = lib.literalExpression '' { daemon.bind_addr = "127.0.0.1:7070"; } ''; description = '' Contents of `~/.config/maestro/config.toml`, in the schema `maestro-config` defines. Every field has a default, so `{}` is valid and writes no file. `daemon.bind_addr` is also the address the browser UI is served on. There is no auth layer — bind it to a LAN/VPN address only on a trusted segment, never a public IP. ''; }; }; config = lib.mkIf cfg.enable { home.packages = [ cfg.package ]; xdg.configFile."maestro/config.toml" = lib.mkIf (cfg.settings != { }) { source = tomlFormat.generate "maestro-config.toml" cfg.settings; }; systemd.user.services = lib.mkIf cfg.daemon.enable { maestro-daemon = { Unit = { Description = "maestro daemon — tmux wrapper + session API + browser UI"; After = [ "network.target" ]; # MUST stay 0: a fast-crashing daemon would burn the default # 5-starts-per-10s budget and park the unit in `failed` until a # manual `systemctl --user reset-failed`. StartLimitIntervalSec = 0; }; Service = { Type = "simple"; WorkingDirectory = "%h"; ExecStart = "${lib.getExe cfg.package} daemon run"; Environment = [ "PATH=${profilePath}" # Keeps the daemon on the same tmux server the user's shell # and TUI use, instead of a second one under /tmp. "TMUX_TMPDIR=%t" ]; Restart = "always"; RestartSec = 2; # If this unit boots before any shell, the shared tmux server # lands in its cgroup; the default control-group kill would take # every session down on restart. KillMode = "process"; }; Install.WantedBy = [ "default.target" ]; }; }; }; }; }