Skip to content

Releases: magnattic/kpxc

v0.4.3 - friendlier first-time picker

30 Apr 21:33

Choose a tag to compare

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 --interactive don'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

30 Apr 11:44

Choose a tag to compare

Bugfix release over v0.4.1 — five critical scope-mode bugs surfaced by an internal review, plus robustness hardening.

Critical fixes

  • kpxc get exited 1 on success. Missing exit 0 in dispatch.
  • fzf picker emitted bare entry titles instead of full paths. Children of nested groups lost their parent context. Switched keepassxc-cli ls -R to -Rf.
  • Scope spec parser word-split paths with spaces (Email/My Personal was 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. New check_clean helper asserts exit 0, empty stderr, and matching stdout.

Robustness

  • kpxc lock refuses to remove the cache directory if it contains files other than master/scoped (defends against collisions on a shared /dev/shm).
  • Refuse early if $KPXC_CACHE is 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

  • -i shortcut for --interactive.
  • Migration warning: _load_scope_file detects 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

30 Apr 10:55

Choose a tag to compare

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 as all in the scope file).
  • fzf is now a soft dependency: required for the picker, but kpxc unlock --master and 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           # macOS

v0.4.0 - scope mode default with interactive picker

30 Apr 10:47

Choose a tag to compare

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 unlock is 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 -p etc. 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

29 Apr 22:35

Choose a tag to compare

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

29 Apr 22:02

Choose a tag to compare

Bug-fix release addressing findings from a code review of v0.2.0. No breaking changes.

Critical fixes

  • kpxc denylists subcommands with non-standard arg shapes (db-create, import, open, close, merge). v0.2.0 would silently reorder arguments — kpxc import source.xml dest.kdbx could overwrite source.xml with the contents of the user's unlocked database.
  • kpxc denylists mutating -p flags (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_* to KP_* (was a copy-paste from before the v0.2.0 rename).
  • kpxc / kpget no longer remove the cache on TTL miss (race with concurrent kpunlock).
  • kpxc passes through --help / -h for any subcommand without DB injection.
  • kpunlock writes cache with trailing newline (defensive).
  • kpget surfaces keepassxc-cli's actual error instead of a generic "cache stale" hint.
  • kplock got set -euo pipefail for consistency.
  • CI shellcheck severity bumped from warning to style.

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

29 Apr 15:13

Choose a tag to compare

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_* to KP_* (KP_DB, KP_KEYFILE, KP_TTL, KP_CACHE, KP_CONFIG). Avoids collision with keepassxc-cli's own KPXC_CONFIG / KPXC_CONFIG_LOCAL.
  • Cache file moved /dev/shm/<uid>-kpcache to /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

28 Apr 18:39

Choose a tag to compare

Initial release.

Three scripts:

  • kpunlock - prompt for the KeePass master password once, cache in tmpfs
  • kpget - retrieve entry fields via the cached password + keepassxc-cli
  • kplock - clear the cache

See README for installation and integration examples (himalaya, mbsync, restic).