# latchd Want a TUI-based login manager because you are tired of GDM or Plasma Login? Latchd gives you a small keyboard-first login screen on a Linux virtual terminal, then starts a TTY shell, an X11 desktop, or a Wayland desktop for the account you choose. Latchd is written in Rust and runs as a systemd service. It finds login accounts through NSS, reads desktop entries from the configured directories, The old Go prototype is still in [`legacy/go/`](legacy/go/), but it is not part of the build. ![Latchd login screen](docs/images/latchd-preview.png) This screen comes from the Docker preview capture; the [UI guide](docs/ui.md) has the keyboard map and the help screen. ## What latchd does - account discovery — reads normal login accounts through NSS; filters low UIDs, excluded names, no-login shells, and unsafe home directories - account status — reads shadow aging fields and the configured faillock helper; shows expiry, lock, inactivity, and failure counts in the screen - session discovery — reads X11 and Wayland `.desktop` files plus administrator scripts; keeps the program and its arguments separate instead of passing them to a shell - login handoff — builds the user environment, opens the configured login session, starts the selected program with the account UID and groups, and closes the session when it exits - X11 startup — creates a private Xauthority file, waits for the X server readiness signal, and removes the file after the desktop exits - Wayland startup — starts the selected desktop with the session values it needs; it does not start a compositor - TTY startup — attaches the selected shell to the virtual terminal and records the user process when UTMPX is available - user interface — provides account switching, last-session preferences, filtering, refresh, notifications, wallpapers, reduced motion, and confirmed power actions Preview mode draws the same screen and reads the same TOML structure, but it does not authenticate or start a user program, which makes it useful for layout work and config checks. ## Quick start Install the build requirements on Debian or Ubuntu: ```sh sudo apt-get install build-essential pkg-config libpam0g-dev libpam-modules ``` Build the release binary and run the checks: ```sh make check make build ``` The binary is `build/latchd`. Open the preview screen with: ```sh ./build/latchd --preview --no-log ``` The repository pins Rust 1.88 in [`rust-toolchain.toml`](rust-toolchain.toml). ## Configuration The service reads `/etc/latchd/config.toml`. Preview mode first checks `$XDG_CONFIG_HOME/latchd/config.toml`, then `~/.config/latchd/config.toml`, and finally the system file. `--config PATH` selects one file directly. Create a user file from the example with: ```sh make install-user ``` That command creates `~/.config/latchd/` with mode `700`, writes `config.toml` with mode `600`, and leaves an existing file alone. The full field reference, variable substitution, command-line overrides, desktop entry rules, and permission checks are in [`docs/configuration.md`](docs/configuration.md). ## Install on a test machine Use a disposable machine and keep another login path available while you change the display manager: ```sh sudo make install sudo systemctl enable latchd.service sudo systemctl start latchd.service ``` The install target writes the binary, `/etc/latchd/config.toml`, `/etc/pam.d/latchd`, the session-script directories, and the systemd unit. The supplied unit uses `/dev/tty2`, so change both `tty` in the TOML file and `TTYPath` in the unit if you use another virtual terminal. The install target does not disable the current display manager for you. ## Keyboard controls | Key | Action | | --- | --- | | Tab / Shift-Tab | Move focus | | Up / Down | Move focus or move in a filtered list | | Left / Right | Change the selected session | | `/` | Filter sessions or accounts | | Ctrl-R | Refresh accounts and sessions | | Ctrl-N / Ctrl-P | Select the next or previous account | | Ctrl-B | Toggle the background | | Ctrl-M | Toggle reduced motion | | Enter | Authenticate or submit a password change | | Esc | Clear the password or close a popup | | `?` | Show the key reference | | Ctrl-C | Restore the terminal and quit | | F1–F12 | Confirm a configured power action | Lowercase filters ignore case, while a filter with an uppercase character is case-sensitive. The preview screen and the capture command are documented in [`docs/ui.md`](docs/ui.md). ## Checks and releases Use `make check` for formatting, unit tests, Clippy, and rustdoc; use `make integration-test` for the CLI paths and `make docker-test` for the pinned container build. `make pam-fixture-test` needs root because it installs temporary test policies, while `make system-e2e` checks the services and devices available on the current host. Run `make system-e2e-strict` only in a disposable VM that has a test policy, a real virtual terminal, systemd-logind, and the X11 or Wayland stack you want to test. [`docs/e2e-testing.md`](docs/e2e-testing.md) explains what each check can and cannot cover. Create a local release archive with: ```sh make package ``` GitLab CI runs the same Rust, CLI, container, and capability checks, then creates an archive and SHA-256 file for a SemVer tag. The release process is described in [`docs/development.md`](docs/development.md). ## Read next - [`docs/guide.md`](docs/guide.md) explains the service from startup to session cleanup. - [`docs/configuration.md`](docs/configuration.md) explains each configuration field. - [`docs/architecture.md`](docs/architecture.md) explains the workspace and the process boundaries. - [`docs/development.md`](docs/development.md) explains builds, tests, Docker, and packages. - [`docs/ui.md`](docs/ui.md) shows the screen and its controls. - [`docs/e2e-testing.md`](docs/e2e-testing.md) explains the VM and container checks. ## Project layout ```text crates/latchd-core/ account discovery, configuration, sessions, and UTMPX crates/latchd-auth/ authentication worker and session handoff crates/latchd-tui/ terminal state, rendering, input, and effects rust/main.rs command-line entry point and service loop extra/ example configuration, login policy, and systemd unit scripts/ CLI, Docker, fixture, and host checks legacy/go/ reference implementation from the early prototype ``` ## License Apache-2.0. See [`LICENSE`](LICENSE).