No description
  • Python 97.1%
  • Shell 2.9%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-11 21:15:01 +03:00
assets Bump to version 0.3.3 2026-10-11 19:28:38 +03:00
docs/screenshots Bump to version 0.3.3 2026-10-11 19:28:38 +03:00
kpassword_store Bump to version 0.5.2 2026-10-11 21:15:01 +03:00
share Bump to version 0.5.2 2026-10-11 21:15:01 +03:00
tests Bump to version 0.5.2 2026-10-11 21:15:01 +03:00
appimage_runtime.sh Bump to version 0.3.3 2026-10-11 19:28:38 +03:00
CHANGELOG.md Bump to version 0.5.2 2026-10-11 21:15:01 +03:00
e2e_test.py Bump to version 0.5.2 2026-10-11 21:15:01 +03:00
LICENSE Bump to version 0.3.3 2026-10-11 19:28:38 +03:00
make_appimage.sh Bump to version 0.5.2 2026-10-11 21:15:01 +03:00
make_demo_store.py Bump to version 0.3.3 2026-10-11 19:28:38 +03:00
make_icon.py Bump to version 0.3.3 2026-10-11 19:28:38 +03:00
pyproject.toml Bump to version 0.5.2 2026-10-11 21:15:01 +03:00
README.md Bump to version 0.5.2 2026-10-11 21:15:01 +03:00
README_ru.md Bump to version 0.5.2 2026-10-11 21:15:01 +03:00
requirements.txt Bump to version 0.3.3 2026-10-11 19:28:38 +03:00
run.sh Bump to version 0.3.3 2026-10-11 19:28:38 +03:00

KPassword-Store

A Qt6 desktop front end for pass — the standard unix password manager. The store, the encryption and the file format are exactly the ones pass uses: GPG-encrypted files in $PASSWORD_STORE_DIR. This application never invents a vault of its own; it reads and writes your existing store through the pass command, so anything you do here is immediately usable from the terminal and vice versa.

  • Version: 0.5.2
  • Author: Orfik
  • Project: https://git.orfik-dmn.ru/orf/KPassword-Store
  • License: GNU General Public License v3 or later — see LICENSE
  • Written with the assistance of the MiniMax AI model (agent Mavis). The author reviewed the result and is responsible for every line.

Requirements

Required: pass and GnuPG

KPassword-Store is a front end, not a password manager of its own. Before the first launch you need a working, initialised pass store:

# Debian / Ubuntu
sudo apt install pass gnupg
# Arch
sudo pacman -S pass gnupg
# Fedora
sudo dnf install pass gnupg2
# macOS
brew install pass gnupg pinentry-mac

Then create the GPG key and initialise the store:

gpg --full-generate-key          # or: gpg --quick-generate-key
pass init your@mail.com          # creates ~/.password-store with a .gpg-id

The application refuses to pretend everything is fine when the store is not ready: Tools → Check Environment reports the pass binary, the gpg binary, the store path, the GPG recipients taken from .gpg-id, whether the store is a git repository, and the number of entries. The same check runs at start-up and raises a notification if something is missing.

Required for two-factor codes: pass-otp

One-time passwords are stored in the pass-otp format, as a separate GPG-encrypted file next to the entry: example.com.otp.gpg next to example.com.gpg. To create and manage those files from the command line you need pass-otp:

# Debian / Ubuntu
sudo apt install pass-otp
# Arch
sudo pacman -S pass-otp
# Fedora
sudo dnf install pass-otp
# macOS
brew install pass-otp
pass otp add example.com        # prompts for the otpauth:// URI
pass otp show example.com       # prints a code

What this application does and does not need it for:

  • Reading a code only requires gpg. The app decrypts <name>.otp.gpg itself and computes HOTP/TOTP locally in pure Python (RFC 4226 / RFC 6238). It does not shell out to pass otp.
  • Creating an OTP file is also possible without pass-otp: paste an otpauth:// link into the notes of an entry and use Promote to OTP file, or edit the secret directly in the OTP details dialog.
  • pass-otp is what you want on the command line — it makes pass ls, pass mv and friends treat the .otp files consistently, and it is the standard way to add a secret in the first place.

One-time codes are never sent anywhere — they are computed on your machine, and the application makes no network requests on its own. The single exception is the git synchronisation you can ask for explicitly (see below); there is no telemetry of any kind.


Installation

Grab the release, mark it executable, run it:

chmod +x KPassword-Store-0.5.2-x86_64.AppImage
./KPassword-Store-0.5.2-x86_64.AppImage

One self-contained file. The interpreter, the standard library, PyQt6 with Qt 6 and the whole X11/xcb stack are inside; only pass and gpg come from the host, which is exactly what the application needs them for.

  • Compatibility: x86_64 Linux with glibc 2.17 or newer — Ubuntu 20.04+, Debian 10+, Fedora 32+.
  • If FUSE is unavailable (containers, some hardened distributions), run ./KPassword-Store-0.5.2-x86_64.AppImage --appimage-extract-and-run.
  • If Qt still cannot reach the display, the launcher says so and prints the exact packages to install instead of dying silently. KPASS_DEBUG_PLATFORM=1 additionally lists the available platform plugins and the libraries that could not be resolved.

From source

git clone https://git.orfik-dmn.ru/orf/KPassword-Store
cd KPassword-Store
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
./run.sh

To build the AppImage yourself you need appimagetool:

APPIMAGETOOL=/path/to/appimagetool ./make_appimage.sh
# if the interpreter with PyQt6 is not in .venv
KPASS_PYTHON=/usr/bin/python3 ./make_appimage.sh

make_appimage.sh assembles the AppDir and verifies it; appimage_runtime.sh puts a relocatable CPython and the X11/xcb stack inside it. Everything downloaded is cached in dist/cache, so a rebuild needs no network.

Desktop entry

mkdir -p ~/.local/share/applications
cp share/kpassword-store.desktop ~/.local/share/applications/
cp assets/kpassword-store-256.png ~/.local/share/icons/hicolor/256x256/apps/kpassword-store.png

Features

Working with entries

  • Create, edit, rename, duplicate and delete entries.
  • Application trash. Deleting moves the encrypted files to a trash folder with metadata instead of removing them. Restore brings an entry back with its OTP file; purge deletes it for good; the trash can be emptied wholesale.
  • Duplicate protection: the app refuses to overwrite an existing entry silently.
  • Path validation: empty names, .., hidden components and a manual .gpg suffix are rejected with a readable message instead of a GPG error.
  • Site / username layout. With the usual site/username convention the login is taken from the last path component and the site from the rest, so the form fills itself in. Write them into the notes when that guess is wrong.

Two-factor codes

  • Codes are read from existing .otp.gpg files and from valid otpauth:// links found in the notes of an entry.
  • Entries with no password at all work: when the file opens with an otpauth:// link, that link is the two-factor secret, and it is shown as a live code instead of being taken for a password. Such an entry says so in the panel instead of showing an empty password field.
  • An invalid link stays visible as ordinary text — it is never swallowed or mangled into a "valid-looking" card.
  • Promote to OTP file moves an inline secret into a real .otp.gpg file and removes the link from the notes, leaving everything else intact.
  • The OTP details dialog shows the issuer, account, algorithm, digits and period, and lets you correct the stored secret.
  • The app is read-only about OTP: it shows and copies codes, it does not generate or change secrets beyond an explicit edit you confirm yourself.

Synchronising the store with git

If the store is a git repository — the usual arrangement for pass-git — two entries under Tools work with the remote:

  • Update from Repository runs pass git pull.
  • Send to Repository runs pass git push.

Both run on a background thread: a remote can be slow, and a GPG passphrase prompt has no terminal to type into. After a successful pull the entry list is reloaded, so what you see is what the remote actually has.

Two things about these commands are worth knowing, because pass hides them:

  • The first push sets the upstream. A branch that tracks nothing is exactly what pass git init leaves behind, and a plain git push there refuses to do anything. The app notices and sends origin <branch> with --set-upstream instead, so the first push just works. After that, plain pass git push is used.
  • A refusal is never silent. pass does not pass git's exit code through, so a failed push or pull returns success with an empty result. The app reads git's own messages and shows them, together with what to do about the usual causes: no remote configured, no upstream yet, diverged history, or a rejected connection.
  • Uncommitted changes stop the push. pass commits after every write, but it cannot do that without a git identity. When user.name/user.email are not configured, the entries land in the working tree and stay there — a push would succeed and carry stale history while the newest passwords never leave the machine. The app checks for that and says so instead of pretending it worked.

The entries are offered but greyed out when the store is not a git repository, with the reason in the tooltip — making the store one is your decision, not the app's. Run pass git init once if you want it, and add a remote the usual way.

Settings → Git Settings shows what pass-git actually works with: the remotes it knows, the current branch and what it tracks. pass-git keeps no settings of its own — they live in the git config of the store — so that dialog reads exactly the same config, and writes changes back through pass git, which is what the sync entries use. When no remote is configured, the same dialog takes the name and the address and creates it, so a store that is ready for pass git init can be finished without opening a terminal. Addresses are checked before they are saved: git@host:user/repo.git and https://host/repo.git are accepted, anything that cannot be an address is refused with a sentence saying what a correct one looks like.

If the local and remote histories have diverged, git refuses to merge and the app shows its message plus a hint: resolve it with pass git pull --rebase from a terminal.

This is the only part of the application that touches the network, and it does so only when you ask it to.

When ssh refuses, the error comes with an explanation rather than just Permission denied (publickey). The app reads ~/.ssh/config the same way ssh does and reports the HostName, user and identity file it will actually use, whether that key exists on disk, and whether an agent is running. It also flags the trap that catches people: OpenSSH resolves ~ through the password database, not through $HOME, so in a sandbox or container the config you are editing is not the config ssh reads — and the app says so when it sees one.

A push takes the working tree with it. pass git init stages its .gitattributes and never commits it, and a commit that failed for lack of a git identity is silent — so a store could hold changes forever while push refused to send anything. The app makes the commit pass would have made, reports how many changes went in, and only complains when committing is genuinely impossible — in which case the error names user.name and user.email.

Password generation

A generator dialog with length, character classes and an entropy readout, plus a live preview. Passwords are the only thing the app ever generates — it will not invent OTP secrets.

Appearance

  • Dark, light and follow the system themes.
  • Colour scheme: own palette or Plasma. The Plasma option takes the background, the panels, three surface steps, the text, both muted text steps, the borders and the semantic colours (success / error / warning) from the active Plasma colour scheme rather than picking them by eye. Whatever the scheme does not define is listed in the settings dialog, so nothing is silently substituted.
  • The accent comes from the Plasma AccentColor, falling back to DecorationFocus. The accent is expanded into a full ramp (hover, press, soft tone) and the label colour is chosen by WCAG contrast, so a pale accent never ends up with white text on white.
  • Interface and monospace font pickers with a live preview inside the list.
  • Radio buttons and checkboxes show a real checkmark instead of an accent-filled blob, so they stay readable with any accent colour.

Interface

  • Menus follow the KDE menu guidelines: File, Edit, View, Tools, Settings, Help — in that order, and only the entries the application can actually perform.
  • English and Russian, switchable live from Help → Switch Application Language or in the settings dialog. The language follows the system locale by default; an unsupported locale falls back to English rather than to a half-translated UI.
  • System tray with optional minimise-to-tray and a tray menu that keeps the same preferences the window has.
  • Auto-lock after a configurable idle time, clipboard clearing after a configurable delay, optional confirmation before deleting.

Keyboard shortcuts

Ctrl+N New entry
Ctrl+E Edit the selected entry
F2 Rename
Ctrl+D Duplicate
Delete Delete (moves to the application trash)
Ctrl+C Copy the password
Ctrl+Shift+C Copy all fields
Ctrl+Shift+P Show or hide the password
Ctrl+F Focus the search field
Ctrl+A Select all entries
F5 / Ctrl+R Reload the tree
Ctrl+L Lock now
Ctrl+, Settings
Ctrl+M Show or hide the menubar
Ctrl+Q Quit

Store layout and entry format

~/.password-store/
├── .gpg-id
├── github.com/
│   └── octocat.gpg
└── mail.ru/
    └── ivan_petrov.gpg

An entry is a plain text file, exactly as pass defines it:

the-password
login: octocat
website: https://github.com

Everything after the first line is free-form. Known key: value lines (login:, website:, email:, url:, autofill:, …) are surfaced as fields; unknown ones and otpauth:// links stay in the notes and are preserved on save.

A two-factor secret lives next to the entry:

github.com/
├── octocat.gpg
└── octocat.otp.gpg      # the pass-otp secret

Development

./tests/run_all.sh

Six suites, 528 checks, no external test runner required:

suite what it covers
tests/test_otp.py RFC 4226 / RFC 6238 vectors, otpauth:// parsing, edge cases
tests/test_core.py pass/gpg integration, CRUD, trash, layout detection, validation
tests/test_kde.py kdeglobals parsing, Plasma 5 and 6, mapping the scheme onto the palette, contrast of the results
tests/test_i18n.py catalogue completeness, placeholder integrity, locale detection and the English fallback
tests/test_settings_gui.py every settings control applied live, including the font pickers
e2e_test.py the whole window end to end, including the menu structure

tests/run_all.sh builds a throwaway demo store when one is not supplied, and rebuilds it if a previous run died halfway and left an empty shell behind.


Screenshots

Main window Plasma light colour scheme
Main window with the Plasma colour scheme The same store under the light Plasma scheme
Settings About
Settings: language, colour scheme, fonts About: GPL-3.0, author, project page
An entry with no password, only a code

Changelog

Every release is described in CHANGELOG.md.


Deleting your data

Deleting an entry never touches the encrypted file immediately: it is moved to the application trash, where it can be restored. Purging removes the file. Emptying the trash calls pass rm --recursive --force, which is the same thing pass rm does — a deleted entry is gone for good, and the trash exists to undo a mistake, not to be a second backup. Keep your own backups.


License

GNU General Public License v3 or later (GPL-3.0-or-later). The full text is in LICENSE.

GPL was chosen deliberately. This application works with your secrets and runs external pass/gpg processes, so anyone distributing a modified build has to publish their changes. That is the guarantee that nobody ends up with a "closed" version silently doing something with your keys.