Commit Diff


commit - 4cd15b53068761f7ea875c81f706a11b42d1620b
commit + cbbc749ce296ab9dc4e0020a6faa95d6c4959016
blob - 28c92d6ad03327f70c9edfa43a2b76184c0da98d
blob + b19c762c63942d472a4416a5f12c401687b3d73c
--- home/ale/home.nix
+++ home/ale/home.nix
@@ -342,6 +342,11 @@ in
     # README, sin necesitar ningún mkOrder manual).
     syntaxHighlighting.enable = true;
     initContent = lib.mkMerge [
+      ''
+        # Secrets locales (API keys, tokens): archivo fuera de /nixdots para
+        # no versionar ni exponer credenciales en el Nix store.
+        [ -f "$HOME/.config/zsh/secrets.zsh" ] && source "$HOME/.config/zsh/secrets.zsh"
+      ''
       # zsh-autosuggestions -- sourceado a mano (NO con la opción nativa
       # programs.zsh.autosuggestion.enable) porque esa opción fija su propio
       # mkOrder en 700, es decir ANTES del compinit de oh-my-zsh (800) y
@@ -784,6 +789,8 @@ in
     zed-editor # editor -- paquete directo de nixpkgs (el binario se llama
       # `zeditor`, no `zed`; el .desktop instalado sí queda como "Zed" en el
       # launcher)
+    tor-browser # paquete directo de nixpkgs -- sin módulo declarativo
+      # propio como zen-browser (programs.zen-browser)
   ];
 
   # --- LibrePods: autostart en segundo plano ---
blob - /dev/null
blob + 1ee66264286f65a298ad85527e80247978f20e7d (mode 644)
--- /dev/null
+++ modules/dms-greeter-fixed.nix
@@ -0,0 +1,396 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib)
+    types
+    mkEnableOption
+    mkOption
+    mkIf
+    mkDefault
+    literalExpression
+    getExe
+    makeBinPath
+    optionalString
+    escapeShellArgs
+    ;
+
+  cfg = config.services.displayManager.dms-greeter;
+  cfgDms = config.programs.dms-shell;
+  cfgAutoLogin = config.services.displayManager.autoLogin;
+  sessionData = config.services.displayManager.sessionData;
+
+  # Miracle WM is nested under `programs.wayland.miracle-wm` unlike the rest
+  compositorOption =
+    if cfg.compositor.name == "miracle-wm" then "wayland.miracle-wm" else "${cfg.compositor.name}";
+  cacheDir = "/var/lib/dms-greeter";
+
+  # Not all compositor packages match the name they use and Miracle does not have a package option
+  compositorPkg =
+    if cfg.compositor.name == "miracle-wm" then
+      pkgs.miracle-wm
+    else
+      lib.attrByPath [
+        "programs"
+        cfg.compositor.name
+        "package"
+      ] null config;
+
+  # PARCHE (ale, 2026-09-15, ver NOTES.md): el módulo upstream
+  # (nixos/modules/services/display-managers/dms-greeter.nix) invoca
+  # "${cfg.package}/bin/dms-greeter", asumiendo el layout de dms-shell >=1.6
+  # (donde el CLI Go `dms` embebe los assets de quickshell y ya no expone un
+  # bin/dms-greeter suelto -- confirmado: ni siquiera pkgs.dms-shell 1.6.1 de
+  # nixpkgs lo genera, ver `cmd/` upstream: solo dms y dankinstall). Este repo
+  # pinea dank-material-shell a v1.5.3 a propósito (flake.nix), cuyo layout
+  # SÍ instala el launcher como archivo suelto en
+  # share/quickshell/dms/Modules/Greetd/assets/dms-greeter, junto con el flag
+  # -p que le indica qué config de quickshell cargar (sin esto, busca la
+  # config nombrada "dms-greeter" en las rutas de búsqueda de quickshell en
+  # vez de la ruta absoluta del paquete). greetd fallaba en boot-loop
+  # ("No existe el fichero o el directorio") en cuanto nixpkgs actualizó el
+  # módulo (nix flake update, sep-2026) sin que este repo hubiera cambiado de
+  # versión de dms-shell. Bug real de nixpkgs, no arreglado río arriba a esta
+  # fecha (nixpkgs#546897 es uno distinto, sobre configHome). Todo lo demás
+  # de este archivo es una copia literal del módulo upstream -- si algún día
+  # se actualiza dank-material-shell a >=1.6 con un launcher equivalente,
+  # basta con quitar este archivo + el disabledModules en desktop.nix.
+  greeterScript = pkgs.writeShellScriptBin "dms-greeter-start" ''
+    export PATH=$PATH:${
+      makeBinPath [
+        cfg.quickshell.package
+        compositorPkg
+        pkgs.glib # provides gdbus, used by the fprintd hardware probe and portal reads
+      ]
+    }
+    ${
+      escapeShellArgs (
+        [
+          "${cfg.package}/share/quickshell/dms/Modules/Greetd/assets/dms-greeter"
+          "--cache-dir"
+          cacheDir
+          "--command"
+          cfg.compositor.name
+          "-p"
+          "${cfg.package}/share/quickshell/dms"
+        ]
+        ++ lib.optionals (cfg.compositor.customConfig != "") [
+          "-C"
+          "${pkgs.writeText "dmsgreeter-compositor-config" cfg.compositor.customConfig}"
+        ]
+      )
+    } ${optionalString cfg.logs.save "> ${cfg.logs.path} 2>&1"}
+  '';
+
+  autoLoginCommand =
+    pkgs.runCommand "dms-greeter-autologin-command"
+      {
+        nativeBuildInputs = [
+          pkgs.gnugrep
+          pkgs.coreutils
+        ];
+      }
+      ''
+        set -euo pipefail
+
+        session="${sessionData.autologinSession}"
+        desktops="${sessionData.desktops}"
+
+        for sessionFile in \
+          "$desktops/share/wayland-sessions/$session.desktop" \
+          "$desktops/share/xsessions/$session.desktop"
+        do
+          if [ -f "$sessionFile" ]; then
+            command="$(grep -m1 '^Exec=' "$sessionFile" | cut -d= -f2- || true)"
+
+            if [ -n "$command" ]; then
+              printf '%s\n' "$command" > "$out"
+              exit 0
+            fi
+          fi
+        done
+
+        echo "dms-greeter autologin: could not resolve Exec for session '$session'" >&2
+        exit 1
+      '';
+
+  jq = getExe pkgs.jq;
+
+  configFilesFromHome =
+    if cfg.configHome != null then
+      [
+        "${cfg.configHome}/.config/DankMaterialShell/settings.json"
+        "${cfg.configHome}/.local/state/DankMaterialShell/session.json"
+        "${cfg.configHome}/.cache/DankMaterialShell/dms-colors.json"
+      ]
+    else
+      [ ];
+in
+{
+  options.services.displayManager.dms-greeter = {
+    enable = mkEnableOption "DankMaterialShell greeter";
+
+    package = lib.mkPackageOption pkgs "dms-greeter" { };
+
+    compositor = {
+      name = mkOption {
+        type = types.enum [
+          "niri"
+          "hyprland"
+          "sway"
+          "mangowc"
+          "miracle-wm"
+          "labwc"
+        ];
+        example = "niri";
+        description = ''
+          The Wayland compositor to run the greeter in.
+
+          The specified compositor must be enabled via its corresponding
+          `programs.<compositor>.enable` option.
+
+          Supported compositors:
+          - niri: A scrollable-tiling Wayland compositor
+          - hyprland: A dynamic tiling Wayland compositor
+          - sway: An i3-compatible Wayland compositor
+          - mango: A dwm-inspired Wayland compositor with modern config options and multiple layouts
+          - miracle-wm: A keyboard-driven Wayland compositor with smooth animations and extensibility
+          - labwc: Lightweight stacking Wayland compositor inspired by Openbox
+        '';
+      };
+
+      customConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          # Niri example
+          input {
+              keyboard {
+                  xkb {
+                      layout "us"
+                  }
+              }
+          }
+        '';
+        description = ''
+          Custom compositor configuration to use for the greeter session.
+
+          This configuration is written to a file and passed to the compositor
+          when launching the greeter. The format and available options depend
+          on the selected compositor.
+
+          Leave empty to use the system's default compositor configuration.
+        '';
+      };
+    };
+
+    configFiles = mkOption {
+      type = types.listOf types.path;
+      default = [ ];
+      example = literalExpression ''
+        [
+          "/home/user/.config/DankMaterialShell/settings.json"
+          "/home/user/.local/state/DankMaterialShell/session.json"
+        ]
+      '';
+      description = ''
+        List of DankMaterialShell configuration files to copy into the greeter
+        data directory at `/var/lib/dms-greeter`.
+
+        This is useful for preserving user preferences like wallpapers, themes,
+        and other settings in the greeter screen.
+
+        ::: {.tip}
+        Use {option}`configHome` instead if your configuration files are in
+        standard XDG locations.
+        :::
+      '';
+    };
+
+    configHome = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "/home/alice";
+      description = ''
+        Path to a user's home directory from which to copy DankMaterialShell
+        configuration files.
+
+        When set, the following files will be automatically copied to the greeter:
+        - `~/.config/DankMaterialShell/settings.json`
+        - `~/.local/state/DankMaterialShell/session.json`
+        - `~/.cache/DankMaterialShell/dms-colors.json`
+
+        If your configuration files are in non-standard locations, use the
+        {option}`configFiles` option instead.
+      '';
+    };
+
+    quickshell = {
+      package = mkOption {
+        type = types.package;
+        default = if cfgDms.enable then cfgDms.quickshell.package else pkgs.quickshell;
+        defaultText = literalExpression ''
+          if config.programs.dms-shell.enable
+          then config.programs.dms-shell.quickshell.package
+          else pkgs.quickshell;
+        '';
+        description = ''
+          The Quickshell package to use for the greeter.
+
+          Defaults to the quickshell package from `programs.dms-shell` if it is enabled,
+          otherwise defaults to `pkgs.quickshell`.
+        '';
+      };
+    };
+
+    logs = {
+      save = mkEnableOption "saving logs from the DMS greeter to a file";
+
+      path = mkOption {
+        type = types.path;
+        default = "/tmp/dms-greeter.log";
+        example = "/var/log/dms-greeter.log";
+        description = ''
+          File path where DMS greeter logs will be saved.
+
+          This is useful for debugging greeter issues. Logs will include
+          output from both the greeter and the compositor.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion =
+          # Assemble the full attribute structure because miracle-wm is not nested in the same location as the others
+          lib.attrByPath (
+            [ "programs" ] ++ lib.splitString "." compositorOption ++ [ "enable" ]
+          ) false config;
+        message = ''
+          DankMaterialShell greeter: The compositor "${cfg.compositor.name}" is not enabled.
+
+          Please enable the compositor via:
+            programs.${compositorOption}.enable = true;
+        '';
+      }
+      {
+        assertion = cfgAutoLogin.enable -> sessionData.autologinSession != null;
+        message = ''
+          dms-greeter auto-login requires services.displayManager.defaultSession to be set,
+          or at least one session in services.displayManager.sessionPackages.
+        '';
+      }
+    ];
+
+    services.greetd = {
+      enable = mkDefault true;
+      settings = {
+        default_session = {
+          user = "dms-greeter";
+          command = getExe greeterScript;
+        };
+        initial_session = mkIf (cfgAutoLogin.enable && (cfgAutoLogin.user != null)) {
+          inherit (cfgAutoLogin) user;
+          command = ''${getExe pkgs.bash} -lc "${config.systemd.package}/bin/systemd-cat $(<${autoLoginCommand})"'';
+        };
+      };
+    };
+
+    environment.systemPackages = [ cfg.package ];
+
+    fonts.packages = with pkgs; [
+      fira-code
+      inter
+      material-symbols
+    ];
+
+    systemd.tmpfiles.settings."10-dms-greeter".${cacheDir}.d = {
+      user = "dms-greeter";
+      group = "dms-greeter";
+      mode = "0750";
+    };
+
+    systemd.services.greetd.preStart =
+      let
+        allConfigFiles = cfg.configFiles ++ configFilesFromHome;
+      in
+      ''
+        cd ${cacheDir}
+        ${lib.concatStringsSep "\n" (
+          lib.map (f: ''
+            if [ -f "${f}" ]; then
+                cp "${f}" .
+            fi
+          '') allConfigFiles
+        )}
+
+        if [ -f session.json ]; then
+            copy_wallpaper() {
+                local path=$(${jq} -r ".$1 // empty" session.json)
+                if [ -f "$path" ]; then
+                    cp "$path" "$2"
+                    ${jq} ".$1 = \"${cacheDir}/$2\"" session.json > session.tmp
+                    mv session.tmp session.json
+                fi
+            }
+
+            copy_monitor_wallpapers() {
+                ${jq} -r ".$1 // {} | to_entries[] | .key + \":\" + .value" session.json 2>/dev/null | while IFS=: read monitor path; do
+                    local dest="$2-$(echo "$monitor" | tr -c '[:alnum:]' '-')"
+                    if [ -f "$path" ]; then
+                        cp "$path" "$dest"
+                        ${jq} --arg m "$monitor" --arg p "${cacheDir}/$dest" ".$1[\$m] = \$p" session.json > session.tmp
+                        mv session.tmp session.json
+                    fi
+                done
+            }
+
+            copy_wallpaper "wallpaperPath" "wallpaper"
+            copy_wallpaper "wallpaperPathLight" "wallpaper-light"
+            copy_wallpaper "wallpaperPathDark" "wallpaper-dark"
+            copy_monitor_wallpapers "monitorWallpapers" "wallpaper-monitor"
+            copy_monitor_wallpapers "monitorWallpapersLight" "wallpaper-monitor-light"
+            copy_monitor_wallpapers "monitorWallpapersDark" "wallpaper-monitor-dark"
+        fi
+
+        if [ -f settings.json ]; then
+            theme_file="$(${jq} -r '.customThemeFile // empty' settings.json)"
+            if [ -f "$theme_file" ] && [ -r "$theme_file" ]; then
+                cp "$theme_file" custom-theme.json
+                mv settings.json settings.orig.json
+                ${jq} '.customThemeFile = "${cacheDir}/custom-theme.json"' settings.orig.json > settings.json
+            fi
+        fi
+
+        mv dms-colors.json colors.json || :
+        chown dms-greeter:dms-greeter * || :
+      '';
+
+    services.displayManager.dms-greeter.configFiles = lib.mkIf (
+      cfg.configHome != null
+    ) configFilesFromHome;
+
+    users.groups.dms-greeter = { };
+    users.users.dms-greeter = {
+      description = "DankMaterialShell greeter user";
+      isSystemUser = true;
+      home = cacheDir;
+      homeMode = "0750";
+      createHome = true;
+      group = "dms-greeter";
+      extraGroups = [ "video" ];
+    };
+
+    security.pam.services.dms-greeter = { };
+
+    hardware.graphics.enable = mkDefault true;
+    services.libinput.enable = mkDefault true;
+  };
+
+  meta.teams = [ lib.teams.danklinux ];
+}