# NixOS module — system-wide install. Headroom itself is a user-scope # daemon (it talks to the user's PipeWire session), so this module's # job is narrow: # # 1. Make the `headroom` binary available on every login's PATH. # 2. Drop the systemd user unit into the system-wide location so a # user can `systemctl --user enable --now headroom` without first # having to use Home Manager. # 3. Ensure the standard audio stack (PipeWire + WirePlumber) is # enabled, since headroom can't function without them. # # For per-user defaults — activeProfile, shipped-profile install, # RT-priority tuning — use the Home Manager module # (`homeModules.default`) instead. The two compose. self: { config, lib, pkgs, ... }: let inherit (lib) mkEnableOption mkOption mkIf types literalExpression ; cfg = config.programs.headroom; in { options.programs.headroom = { enable = mkEnableOption "Headroom — automatic loudness and per-app volume control for PipeWire"; package = mkOption { type = types.package; default = self.packages.${pkgs.stdenv.hostPlatform.system}.headroom; defaultText = literalExpression "headroom.packages.\${pkgs.system}.headroom"; description = '' The headroom package to install system-wide. ''; }; }; config = mkIf cfg.enable { # Binary + manpages (when we have them) on the global PATH. environment.systemPackages = [ cfg.package ]; # Make the shipped systemd user unit discoverable by `systemctl # --user`. Setting `packages` here is the canonical NixOS way to # install user-scope unit files from a package — it materialises # `/etc/systemd/user/headroom.service` pointing at the package's # `lib/systemd/user/headroom.service`. systemd.packages = [ cfg.package ]; # Headroom requires PipeWire; refuse to evaluate the module if # the user enabled headroom but not pipewire, with a pointer # rather than a confusing runtime failure. assertions = [ { assertion = config.services.pipewire.enable; message = '' programs.headroom.enable requires services.pipewire.enable = true; headroom is a PipeWire-only daemon. ''; } ]; }; }