From ca63bc9544134a0a2be85ab41454b6271469cd81 Mon Sep 17 00:00:00 2001 From: rope Date: Sun, 23 Aug 2026 19:55:10 +0100 Subject: [PATCH] quickshell: drop redundant keyring unlock, pam already does it in auth Co-Authored-By: Claude Opus 5 --- settings/hyprland.nix | 13 ++++---- settings/quickshell.nix | 68 +++++++++++++---------------------------- 2 files changed, 27 insertions(+), 54 deletions(-) diff --git a/settings/hyprland.nix b/settings/hyprland.nix index ec8580f..4503c9e 100644 --- a/settings/hyprland.nix +++ b/settings/hyprland.nix @@ -329,13 +329,12 @@ in hl.exec_cmd("wl-paste --type image --watch cliphist store") hl.exec_cmd("hyprctl setcursor Bibata-Modern-Ice 24") -- Spotify is NOT started here. It asks the Secret Service for its - -- credentials the moment it launches, which at this point is - -- before the lockscreen has been dismissed and therefore before - -- the login keyring is unlocked. Lock.qml unlocks the keyring with - -- --replace, and that kills the daemon spotify would already be - -- mid-request with, leaving it stuck half-loaded. So Lock.qml - -- launches it instead, right after the unlock succeeds. The window - -- rule below still swallows it onto special:music, and the + -- credentials the moment it launches, and at this point the + -- lockscreen has not been dismissed yet, so the login keyring is + -- still locked and it lands on gcr's unlock prompt instead of + -- loading. Lock.qml starts it after pam accepts the password + -- (pam_gnome_keyring unlocks the keyring in its auth phase). The + -- window rule below still swallows it onto special:music, and the -- respawn-on-close rule above is unaffected. -- Scratchpad terminal: parked on special:scratch at -- login so SUPER+Return is always an instant toggle and the shell diff --git a/settings/quickshell.nix b/settings/quickshell.nix index 9364823..a345eae 100644 --- a/settings/quickshell.nix +++ b/settings/quickshell.nix @@ -638,51 +638,22 @@ in onExited: code => { if (code === 0) { root.bootLock = true; lock.locked = true; } } } - // Chromium/Electron apps ask the Secret Service for their - // credentials as soon as they launch — spotify autostarts, so - // it hit gcr's unlock dialog every boot. The login keyring is - // still locked at that point: greetd's autologin gave - // pam_gnome_keyring no password to stash, and PamContext only - // runs pam_authenticate, never the session phase that - // auto_start unlocks from. So feed the daemon the password - // ourselves once PAM has accepted it. + // Nothing here unlocks the keyring — pam_gnome_keyring already + // did it during the *auth* phase. gkr-pam-module.c's + // pam_sm_authenticate calls unlock_keyring() directly and logs + // "gkr-pam: unlocked login keyring"; the session phase is only + // its fallback for when that fails. So PamContext's auth-only + // run has always been enough, and an explicit + // gnome-keyring-daemon --replace --unlock here was both + // redundant and a trap: the replacement daemon inherits + // quickshell's stdout, holds the pipe open for the life of the + // session, and the Process therefore never reports exiting. // - // --replace is load-bearing, not decoration. gkd_control_unlock() - // exists but is never called from gkd-main.c — only the - // frob-control-* test binaries use it, and those aren't - // installed — so nothing on the CLI can unlock a daemon that is - // already running. --start would be the "talk to the running - // daemon" flag, but parse_arguments() explicitly rejects it - // next to --unlock. Bare --unlock therefore just tries to become - // a second daemon and dies on the already-taken control socket, - // which is precisely what it did here. So: take over from the - // daemon greetd's PAM started, and unlock on the way up. - // - // No trailing newline on the write — read_login_password() reads - // stdin to EOF and its own comment says "All data (including - // newlines) are part of the password". - Process { - id: keyringUnlock - property string secret: "" - command: [ "${pkgs.gnome-keyring}/bin/gnome-keyring-daemon", "--replace", "--unlock" ] - onStarted: { - write(secret); - secret = ""; - // EOF, or the daemon blocks on that read forever. - stdinEnabled = false; - } - // The Secret Service is up and unlocked by the time the - // parent exits (it forks, then exits 0 once the child - // signals ready). Only now is it safe to start spotify — - // see the note in settings/hyprland.nix's startup handler - // for why it no longer autostarts there. - onExited: { - if (!root.bootLock) return; - root.bootLock = false; - spotifyStart.running = true; - } - } - + // The only real problem was ordering. Spotify asks the Secret + // Service the moment it launches, and from hyprland.start that + // is before the lockscreen has been dismissed — keyring still + // locked, so it lands on gcr's unlock prompt. Start it here, + // after PAM has accepted the password, instead. Process { id: spotifyStart // Via hyprland, not as a child of quickshell: a shell @@ -700,10 +671,13 @@ in } onCompleted: result => { if (result === PamResult.Success) { - keyringUnlock.secret = root.password; - keyringUnlock.stdinEnabled = true; - keyringUnlock.running = true; lock.locked = false; + // Boot lock only: a later manual lock/unlock must + // not spawn a second spotify. + if (root.bootLock) { + root.bootLock = false; + spotifyStart.running = true; + } } else root.failed = true; root.password = ""; }