pmenu is a Rust password picker with runtime-selectable backends.
It compiles a single binary and selects concrete implementations for menu, password store, clipboard, and autofill at runtime through CLI flags or a config file.
- Menu backends:
wofi,fuzzel,bemenu - Password store backends:
passage,pass - Clipboard backends:
wl-clipboard,xclip - Autofill backends:
wtype - Config file support with CLI override precedence
- Reusable
corelibrary with backend traits and flow orchestration - Built-in
password,username,url, andfillselections - Best-effort context-aware initial search for qutebrowser, Discord, and Steam on Linux
src/core/: backend traits, domain types, errors, and the main picker flowsrc/cli/: CLI parsing, config loading, notifications, and concrete process-backed backendsflake.nix: Nix dev shell and package definition
With Nix:
nix develop -c cargo test
nix build .#defaultWithout Nix:
cargo build
cargo testThe compiled binary is named pmenu.
Run with defaults:
pmenuSelect backends explicitly:
pmenu \
--store-backend passage \
--menu-backend wofi \
--clipboard-backend wl-clipboard \
--autofill-backend wtypeTrigger autofill instead of copy:
pmenu --action autofillSelect a field directly without opening the second menu:
pmenu --field fillEnable built-in tracing:
pmenu --trace--config <path>--store-backend <name>--store-path <path>--store-identities-file <path>--menu-backend <name>--clipboard-backend <name>--autofill-backend <name>--clip-time <seconds>--field <name>--action <copy|autofill>--no-notify--trace
CLI flags override config file values.
Default config path:
~/.config/pmenu/config.toml
Example:
[store]
backend = "passage"
path = "~/.passage/store"
identities_file = "~/.passage/identities"
[menu]
backend = "wofi"
[clipboard]
backend = "wl-clipboard"
clip_time_secs = 45
[autofill]
backend = "wtype"Defaults when no config is present:
- store backend:
passage - menu backend:
wofi - clipboard backend:
wl-clipboard - autofill backend:
wtype - action:
copy - clip timeout:
45
passstores are read from.gpgfiles.passagestores are read from.agefiles.- File-based stores skip hidden files and directories.
- The picker exposes built-in
usernameandurlfields derived from entry metadata or the entry path. - The
filloption types username, then tab, then password when an autofill backend is available. - On Linux,
wofican open with an initial search based on the focused app; qutebrowser, Discord, and Steam are recognized. ~is expanded in configured paths.- Notifications use
notify-sendon Linux when enabled. --traceemits detailed logs to stderr without printing secret values.RUST_LOGcan be used instead of--tracefor custom log filtering.- Missing external tools fail at runtime with a clear command-specific error.