Files
deckshift/docs/session-backend-handoff.md
2026-07-11 17:49:19 -04:00

12 KiB

DeckShift Session Backend Handoff

Last updated: 2026-07-11.

Goal

Extend DeckShift so its Steam Deck-style gaming mode is not hard-wired to SDDM. The first target is to preserve the existing SDDM behavior for upstream/Omarchy users while adding a DMS/greetd manual backend for our Hyprland/DMS dotfiles.

The long-term goal is:

  • sddm: existing seamless auto-login flow for Omarchy users.
  • dms-greeter-manual: select the next session through DMS greeter memory and require normal greeter authentication.
  • Later: a host-gated one-shot auto-login backend for living-room machines, if it can be made safe and recoverable.

Repository State

Local checkout:

/home/anodyine/Projects/deckshift

Current work branch:

feature/session-backends

Remotes:

origin   ssh://git@tea.kyle-merritt.com:2222/anodyine/deckshift.git
github   https://github.com/Anodyine/deckshift.git
upstream https://github.com/28allday/deckshift.git

origin is the Gitea working remote. github is the public fork. upstream is fetch-only for the original DeckShift project.

What Changed So Far

All code changes so far are in:

deckshift.sh
README.md

Installer Configuration

deckshift.sh now accepts these variables:

DECKSHIFT_SESSION_BACKEND=auto
DECKSHIFT_DESKTOP_SESSION_ID=hyprland-uwsm.desktop
DECKSHIFT_GAMING_SESSION_ID=gamescope-session-steam-nm.desktop

Supported backend values right now:

auto
sddm
dms-greeter-manual

auto detects DMS/greetd when:

  • /etc/systemd/system/display-manager.service resolves to greetd.service.
  • /etc/greetd/config.toml contains dms-greeter.

Otherwise it falls back to sddm.

Generated Backend Config

The installer writes:

/etc/deckshift/session-backend.conf

It records the active backend, desktop/gaming session IDs, the Gamescope session file path, SDDM config path, and DMS greeter memory path.

Generated Helpers

The old SDDM-specific calls are now routed through backend-neutral helpers:

/usr/local/bin/deckshift-session-switch
/usr/local/bin/deckshift-session-transition

/usr/local/bin/gaming-session-switch is still generated as a compatibility wrapper that execs deckshift-session-switch.

Existing generated scripts now call the backend-neutral helpers:

/usr/lib/os-session-select
/usr/local/bin/switch-to-gaming
/usr/local/bin/switch-to-desktop

SDDM Backend Behavior

The SDDM backend preserves the previous model:

  1. Write or update /etc/sddm.conf.d/zz-gaming-session.conf.
  2. deckshift-session-switch gaming|desktop changes Session=....
  3. deckshift-session-transition runs sudo -n systemctl restart sddm.

This should be behaviorally equivalent to the original DeckShift flow.

DMS Manual Backend Behavior

The DMS manual backend does not create SDDM config.

deckshift-session-switch gaming|desktop:

  1. Resolves the target session file from an allowlist:

    • desktop: DECKSHIFT_DESKTOP_SESSION_ID
    • gaming: DECKSHIFT_GAMING_SESSION_ID
  2. Looks in:

    • /usr/local/share/wayland-sessions
    • /usr/share/wayland-sessions
  3. Writes DMS greeter memory:

    /var/cache/dms-greeter/.local/state/memory.json
    
  4. Preserves any existing JSON keys and updates:

    • lastSessionId
    • lastSessionDesktopId

deckshift-session-transition:

  1. If XDG_SESSION_ID exists, runs:

    loginctl terminate-session "$XDG_SESSION_ID"
    
  2. If there is no current session ID, falls back to:

    sudo -n systemctl restart greetd.service
    

This intentionally returns the user to the DMS greeter for normal authentication. It is not a one-shot auto-login implementation.

Sudoers

The generated sudoers file now includes NOPASSWD entries for:

/usr/local/bin/deckshift-session-switch
/usr/local/bin/deckshift-session-transition
/usr/bin/systemctl restart sddm
/usr/bin/systemctl restart greetd.service

The existing compatibility and NetworkManager/Bluetooth rules remain.

README

The README now documents:

  • The experimental DMS/greetd manual backend.
  • The new backend config and helper files.
  • The backend-neutral session switching flow.
  • Updated uninstall cleanup for /etc/deckshift and the new helper scripts.

Validation Already Run

These checks passed:

bash -n deckshift.sh
git diff --check

The generated heredoc scripts were also extracted and syntax-checked with bash -n:

  • deckshift-session-switch
  • deckshift-session-transition
  • switch-to-gaming
  • switch-to-desktop
  • os-session-select
  • gaming-session-switch compatibility wrapper

shellcheck was not installed on this machine, so it has not been run yet.

No installer run has been performed. No live /etc, /usr/local/bin, greeter, or display-manager state was changed by this work.

DMS Greeter Selection Probe

After the first code pass, a manual DMS greeter selection test was run outside this checkout. The greeter selected the expected Deckshift dms test session.

That confirms the core DMS manual backend assumption still holds: writing DMS greeter memory can preselect a non-SDDM session for the next login. This does not yet prove the full DeckShift-generated helper flow or Gamescope session entry, but it is the right green light for implementing and testing the manual DMS backend path before any auto-login work.

DMS Generated Helper Probe

The generated helper path was then tested manually:

sudo -n /usr/local/bin/deckshift-session-switch gaming
/usr/local/bin/deckshift-session-transition gaming

It behaved as expected: the current session exited, DMS greeter appeared, and the target gaming session was preselected.

This proves the first real DMS manual backend flow at the DeckShift helper layer. Remaining DMS validation should focus on the full switch-to-gaming script, Gamescope/Steam session startup, and the return path back to the Hyprland UWSM desktop session.

DMS Manual Round Trip

The DMS manual backend was also tested in both directions. It successfully switched the greeter selection away from Hyprland UWSM and then back to the Hyprland UWSM session. The user was able to log back into the UWSM desktop.

This proves the non-SDDM manual session-selection loop:

Hyprland UWSM -> selected gaming/test session -> DMS greeter
DMS greeter/session -> selected Hyprland UWSM -> DMS greeter -> Hyprland UWSM

The next layer to test is no longer DMS memory/session preselection. It is the real DeckShift gaming session: switch-to-gaming, Gamescope/Steam startup, and switch-to-desktop return behavior.

Important Constraints

Do not implement one-shot DMS auto-login yet. Manual DMS switching is the first safe milestone.

Do not make this dotfiles-specific inside DeckShift. The DeckShift fork should grow generic backend seams; the dotfiles repo can decide whether and how to install/use the DMS backend.

Keep the SDDM behavior stable for existing users. The backend abstraction should be boring and low-risk for the default Omarchy path.

Known Gaps And Risks

Not Tested Live

The backend helpers have only been syntax-checked. They still need live testing on:

  • An SDDM/Omarchy machine or VM.
  • A DMS/greetd machine or VM.
  • A real target host with Gamescope/Steam.

DMS Memory Contract

The DMS greeter memory write is based on a local proof from the dotfiles repo: writing lastSessionId and lastSessionDesktopId successfully preselected a test session in DMS greeter.

Still verify:

  • Ownership and permissions after DeckShift writes the file.
  • Whether DMS ever overwrites the file during greeter startup.
  • Whether preserving extra JSON keys is sufficient across DMS versions.

Transition Semantics

For DMS manual mode, loginctl terminate-session "$XDG_SESSION_ID" is preferred. The fallback systemctl restart greetd.service is more disruptive and should be treated as a fallback only.

Verify whether switch-to-desktop from inside Gamescope has a valid XDG_SESSION_ID. If not, returning from gaming may restart greetd.

Session File Location

The installer currently writes the gaming session to:

/usr/share/wayland-sessions/gamescope-session-steam-nm.desktop

For our dotfiles, /usr/local/share/wayland-sessions may be a better long-term owner boundary. Decide whether DeckShift should make this configurable or leave the current upstream-compatible path alone.

Verification Mode

verify_installation still treats /etc/sddm.conf.d/zz-gaming-session.conf as optional by description only. The verification logic marks missing optional files by checking whether the description contains (optional), so this may need adjustment if verifying a DMS backend install.

Documentation Still Has Historical SDDM Mentions

Some README changelog and troubleshooting sections still mention SDDM because they describe older releases or SDDM-specific recovery. That is probably fine, but before publishing this branch upstream, review the README for places where current behavior should say "session backend" instead.

  1. Install shellcheck on the dev machine or run it in a container/VM:

    shellcheck deckshift.sh
    shellcheck bin/deckshift-settings
    
  2. Test SDDM backend generation without changing behavior:

    DECKSHIFT_SESSION_BACKEND=sddm ./deckshift.sh
    

    Verify:

    • /etc/deckshift/session-backend.conf
    • /etc/sddm.conf.d/zz-gaming-session.conf
    • /usr/local/bin/deckshift-session-switch
    • /usr/local/bin/deckshift-session-transition
    • Existing Super+Shift+S / Super+Shift+R behavior.
  3. Test DMS backend in the CachyOS/DMS VM or sacrificial host:

    DECKSHIFT_SESSION_BACKEND=dms-greeter-manual ./deckshift.sh
    

    Verify before switching:

    • Gaming session appears in the DMS greeter session list.
    • deckshift-session-switch gaming writes the expected DMS memory.
    • deckshift-session-switch desktop restores the Hyprland UWSM session.
    • File ownership/group/mode allow DMS greeter to read the memory file.
  4. Test DMS manual transition without Steam first:

    sudo -n /usr/local/bin/deckshift-session-switch gaming
    /usr/local/bin/deckshift-session-transition gaming
    

    Expected result:

    • Current session exits.
    • DMS greeter appears.
    • Gaming session is preselected.
    • User must authenticate manually.
  5. Test return path:

    sudo -n /usr/local/bin/deckshift-session-switch desktop
    /usr/local/bin/deckshift-session-transition desktop
    

    Expected result:

    • Current session exits.
    • DMS greeter appears.
    • Hyprland UWSM is preselected.
  6. Only after manual DMS mode works, test full Gamescope/Steam entry and return.

  7. After both SDDM and DMS manual mode are proven, split large generated helper bodies into clearer functions or files if desired. Avoid refactoring before the behavior is proven.

  8. Add Gitea issues for follow-up work:

    • Prove SDDM backend parity.
    • Prove DMS manual backend in VM.
    • Fix verification mode for backend-specific expected files.
    • Decide session file owner path.
    • Prototype one-shot DMS/greetd auto-login separately.

Recovery Notes For DMS Testing

If a DMS switch leaves the host at the wrong selected session, switch to a TTY and write DMS memory back to Hyprland UWSM:

sudo python3 - <<'PY'
import json
path = "/var/cache/dms-greeter/.local/state/memory.json"
data = {}
try:
    with open(path) as fh:
        data = json.load(fh)
except Exception:
    pass
data["lastSessionId"] = "/usr/local/share/wayland-sessions/hyprland-uwsm.desktop"
data["lastSessionDesktopId"] = "hyprland-uwsm.desktop"
with open(path, "w") as fh:
    json.dump(data, fh, indent=2)
    fh.write("\n")
PY
sudo chgrp greeter /var/cache/dms-greeter/.local/state /var/cache/dms-greeter/.local/state/memory.json 2>/dev/null || true
sudo chmod 0664 /var/cache/dms-greeter/.local/state/memory.json
sudo systemctl restart greetd.service

Do not rely on chezmoi apply as the only recovery path. The failure mode may be that the graphical desktop is unavailable.