Releases: magnattic/kpxc
v0.4.3 - friendlier first-time picker
UX polish for the unlock picker. No functional changes — behaviour and CLI surface identical to v0.4.2.
Changes
-
Picker header rewritten. The previous header explained the how (TAB/ENTER/ESC) but not the what and why. New header opens with the goal:
Pick which entries to cache for
kpxc get(mail, backup, SSH, ...).
Your selection is saved; the master password is discarded afterwards.TAB to mark · ENTER to confirm · ESC to cancel
Or mark the top option to cache the master password (full DB access). -
Sentinel renamed.
[ all entries -> master mode ](cryptic arrow + "instead") became★ Cache master password (full DB access). The leading★still cannot appear in keepassxc paths, so the exact-string compare against the user's selection stays unambiguous. -
First-run setup banner. On the very first
kpxc unlock(no scope file exists yet), kpxc prints a two-line preamble before opening the picker:First-time setup: pick which credentials to cache for CLI use.
Master password is discarded after this -- only your selections stay.Re-picks via
kpxc unlock --interactivedon't show the banner — the user already knows the drill.
Compatibility
Drop-in replacement for v0.4.2. No CLI, env-var, or scope-file changes.
v0.4.2 - scope-mode bugfixes
Bugfix release over v0.4.1 — five critical scope-mode bugs surfaced by an internal review, plus robustness hardening.
Critical fixes
kpxc getexited 1 on success. Missingexit 0in dispatch.- fzf picker emitted bare entry titles instead of full paths. Children of nested groups lost their parent context. Switched
keepassxc-cli ls -Rto-Rf. - Scope spec parser word-split paths with spaces (
Email/My Personalwas split into two args). Rewritten as a stdin-based parser. :was treated as path/field separator inside scope-file lines, but KeePass entry titles can legitimately contain:. Breaking: in-line separator is now a literal TAB. See migration below.- Scope-mode positive tests silently masked failures. They used
"$(kpxc get …)"which only captures stdout; non-zero exit codes and stderr warnings vanished. Newcheck_cleanhelper asserts exit 0, empty stderr, and matching stdout.
Robustness
kpxc lockrefuses to remove the cache directory if it contains files other thanmaster/scoped(defends against collisions on a shared/dev/shm).- Refuse early if
$KPXC_CACHEis a symlink or non-directory. - Detect mixed picker selection (the "all entries" sentinel marked alongside real entries).
- Explicit conflict detection for incompatible flag combos (
--master+--interactive,--master+ scope args,--interactive+ scope args).
Other
-ishortcut for--interactive.- Migration warning:
_load_scope_filedetects likely legacy:-separator lines and prints a one-time hint to stderr. - README documents the TAB-separated scope-file syntax + adds an "Upgrading from v0.4.1" section.
Migration from v0.4.1
The scope file's in-line separator changed from : to TAB. Default-field entries (Email/personal with no field suffix) carry over unchanged. Entries with explicit fields need updating:
# v0.4.1
Email/personal:Username
# v0.4.2
Email/personal Username # literal TAB before "Username"
Easiest: kpxc unlock --interactive regenerates the scope file in the new format. Or hand-edit ~/.config/kpxc/scope.
kpxc will print a one-time warning if it detects likely legacy lines.
Test suite
62 integration tests, all green. Test fixtures cover entries with spaces, entries with : in the title, multi-field TAB-separated scope lines, comment stripping, the legacy-format migration warning (positive + false-positive checks), and the lock-with-stray-files refusal.
v0.4.1 - fzf picker for unlock
The numbered-list picker from v0.4.0 doesn't scale to real KeePass databases. Replaced with fzf multi-select.
Changes
kpxc unlock(first run /--interactive) now opens an fzf multi-select picker. TAB to mark, ENTER to confirm, ESC to cancel. Ctrl+A / Ctrl+D select-all / deselect-all. 80% screen height, reverse layout.- Special sentinel
[ all entries -> master mode ]at the top of the picker. Mark it to flip into master mode (saved asallin the scope file). - fzf is now a soft dependency: required for the picker, but
kpxc unlock --masterand pre-creating the scope file by hand both work without it. - When fzf is missing, the picker prints a clear install hint and the alternatives.
Install fzf
sudo apt install fzf # Debian/Ubuntu
sudo pacman -S fzf # Arch
sudo dnf install fzf # Fedora
brew install fzf # macOSv0.4.0 - scope mode default with interactive picker
The biggest security improvement available without hardware tokens.
Two unlock modes, scope is now the default
Scope mode (default)
Cache only the entries you opted into; master password is used briefly during unlock and discarded immediately. An attacker reading the cache only gets those entries — the rest of the database stays safe even if your user account is compromised.
Master mode (opt-in via --master)
Cache the master password for full keepassxc-cli access (ls, search, edit, add). Convenient for interactive sessions; weaker against a compromised user account.
First-run picker
$ kpxc unlock
KeePass master password: ****
Available entries:
1) Email/personal
2) Email/work
3) Servers/prod
4) Backup/restic
5) Banking/main
Which entries should kpxc cache without re-prompting?
Numbers (e.g. 1,3,5), exact names, patterns (e.g. 'Email/*'),
or 'all' for full master-mode access.
> 1,4
Saved scope to ~/.config/kpxc/scope.
Cached 2 scoped credential(s).
Master password discarded.
The choice is persisted at ~/.config/kpxc/scope and reused on every later kpxc unlock. Edit the file by hand to add fields, or rerun kpxc unlock --interactive to repick.
Subcommands
kpxc unlock # picker on first run, then saved
kpxc unlock --interactive # re-pick scope
kpxc unlock Email/personal Backup/restic # one-off scope (does NOT save)
kpxc unlock --master # cache master password (full access)
kpxc get <entry> [-a <field>] # read cached value
kpxc scope # show what's cached & saved
kpxc lock # clear caches
kpxc <subcmd> [args...] # forward (master mode only)
Breaking changes from v0.3.x
- Default mode flipped:
kpxc unlockis now scope-mode-first. To get the old behavior:kpxc unlock --master. - Cache moved from
/dev/shm/<uid>-kpxc(file) to/dev/shm/<uid>-kpxc.d/{master,scoped}(directory). The legacy file is detected with a clean error. - New env vars:
KPXC_SCOPE(path of the saved scope file). kpxc add -p / edit -p / db-edit -petc. denylists carried over from v0.2.
Tests
51 cases, scope-mode coverage including out-of-scope refusal, generic-subcommand refusal, base64 round-trip with embedded tabs/newlines, scope-output value-leak check, scope-file permission rejection.
v0.3.0 - single kpxc binary with subcommands
Breaking-change release: three sibling commands collapsed into one binary with subcommands.
Breaking changes from v0.2.x
Commands consolidated
| Old | New |
|---|---|
kpunlock |
kpxc unlock |
kplock |
kpxc lock |
kpget <entry> |
kpxc get <entry> |
kpxc <subcmd> |
unchanged |
Single entry point, git/kubectl-style. Only one symlink to install.
Env vars renamed KP_* to KPXC_*
| Old | New |
|---|---|
KP_DB |
KPXC_DB |
KP_KEYFILE |
KPXC_KEYFILE |
KP_TTL |
KPXC_TTL |
KP_CACHE |
KPXC_CACHE |
KP_CONFIG |
KPXC_RC |
KPXC_RC (not KPXC_CONFIG) for the rc file because KPXC_CONFIG and KPXC_CONFIG_LOCAL are reserved by keepassxc-cli itself.
Cache file path (/dev/shm/<uid>-kpxc) and config dir (~/.config/kpxc/) are unchanged.
Migration
# Remove old symlinks
rm ~/.local/bin/{kpunlock,kpget,kplock}
# Single new symlink (the only one needed now)
ln -s ~/.local/share/kpxc/bin/kpxc ~/.local/bin/kpxc
# Update env-var names in your config
sed -i 's/^KP_/KPXC_/' ~/.config/kpxc/config
# Update consumers (himalaya, mbsync, restic, ...)
# kpget <entry> -> kpxc get <entry>Tests
Test suite grew from 28 to 31. All green.
v0.2.1 - security review fixes
Bug-fix release addressing findings from a code review of v0.2.0. No breaking changes.
Critical fixes
kpxcdenylists subcommands with non-standard arg shapes (db-create,import,open,close,merge). v0.2.0 would silently reorder arguments —kpxc import source.xml dest.kdbxcould overwritesource.xmlwith the contents of the user's unlocked database.kpxcdenylists mutating-pflags (add -p,edit -p,db-edit -p,db-edit --set-password). v0.2.0 would silently consume EOF on the prompt for the new password and create entries with empty passwords.
Security fixes
- All four scripts refuse to source a config file unless owned by the current user with no group/world write bits. Prevents code injection via a writable
~/.config/kpxc/config. - README config example now sets
chmod 600.
Other fixes
- README env-var names corrected from
KPXC_*toKP_*(was a copy-paste from before the v0.2.0 rename). kpxc/kpgetno longer remove the cache on TTL miss (race with concurrentkpunlock).kpxcpasses through--help/-hfor any subcommand without DB injection.kpunlockwrites cache with trailing newline (defensive).kpgetsurfaceskeepassxc-cli's actual error instead of a generic "cache stale" hint.kplockgotset -euo pipefailfor consistency.- CI shellcheck severity bumped from
warningtostyle.
Tests
Test suite grew from 15 to 28 tests covering all denylist cases, config permission rejection, and --help passthrough.
v0.2.0 - rename to kpxc + generic wrapper
Project renamed from kpcache to kpxc. Adds the kpxc generic wrapper around any keepassxc-cli subcommand using the cached master password.
Breaking changes from v0.1.0
- Env vars renamed
KPCACHE_*toKP_*(KP_DB,KP_KEYFILE,KP_TTL,KP_CACHE,KP_CONFIG). Avoids collision withkeepassxc-cli's ownKPXC_CONFIG/KPXC_CONFIG_LOCAL. - Cache file moved
/dev/shm/<uid>-kpcacheto/dev/shm/<uid>-kpxc. - Config dir moved
~/.config/kpcache/to~/.config/kpxc/.
New
bin/kpxc- generic wrapper.kpxc ls /Email,kpxc search foo,kpxc db-info, etc.tests/test.sh- integration suite (15 tests, builds an ephemeral KDBX, exercises all paths end-to-end).- CI runs both shellcheck and the integration tests.
- README states this is an unofficial third-party wrapper (not affiliated with KeePassXC).
Migration
# rename cache file (or just re-run kpunlock)
mv /dev/shm/$(id -u)-kpcache /dev/shm/$(id -u)-kpxc 2>/dev/null
# move config dir
mv ~/.config/kpcache ~/.config/kpxc 2>/dev/null
# rename env vars in your config
sed -i 's/KPCACHE_/KP_/g' ~/.config/kpxc/config
# update install location and symlinks
mv ~/.local/share/kpcache ~/.local/share/kpxc
rm ~/.local/bin/kp{unlock,get,lock}
ln -s ~/.local/share/kpxc/bin/kp{unlock,get,lock,xc} ~/.local/bin/v0.1.0 - initial release
Initial release.
Three scripts:
kpunlock- prompt for the KeePass master password once, cache in tmpfskpget- retrieve entry fields via the cached password + keepassxc-clikplock- clear the cache
See README for installation and integration examples (himalaya, mbsync, restic).