README
¶
kpx
kpx is a focused Go CLI for working with KeePassXC-compatible KDBX4 password databases.
It aims to be small, scriptable, and easy to audit:
- CLI only
KDBX4only- macOS-first
- safe atomic saves
- secrets redacted by default
Status
kpx is usable today for password-only KDBX4 workflows.
Current release: v0.1.11
The project is still maturing, and the CLI surface, output details, config behavior, and internal Go APIs may change between early releases.
Overview
kpx is intentionally narrower than a full KeePass desktop replacement. The goal is a dependable command-line tool for people who want to:
- create and maintain local encrypted password databases
- script common vault operations
- inspect and update entries without leaving the terminal
- keep compatibility with KeePassXC workflows
Desktop GUI work is out of scope for this project. kpx is intended to remain a CLI-first tool rather than grow into a desktop application.
Implemented today:
- create a new database
- validate and open an existing database
- list and create groups
- list, show, add, edit, and delete entries
- search entries by title
- store an optional default database in
$XDG_CONFIG_HOME/kpx/config.yml - support optional default
revealbehavior in$XDG_CONFIG_HOME/kpx/config.yml - support optional master password caching for a configured number of seconds
- back up the database before saving, with configurable destination and filename format
- omit the database argument for vault commands when a default is configured
- export a printable plaintext recovery document with secrets for secure paper backup
- emit JSON output for supported commands with
--json - show the CLI version via
kpx versionandkpx --version - atomic save behavior
- a refactored package layout with smaller command and vault files
- round-trip and fixture-based test coverage
- official on-demand KeePassXC compatibility fixtures
- advisory locking for cooperating
kpxprocesses during reads and writes
Install
Install the latest version with Go:
go install github.com/whosthatknocking/kpx@latest
Install a specific version:
go install github.com/whosthatknocking/kpx@v0.1.11
Build from source:
git clone https://github.com/whosthatknocking/kpx.git
cd kpx
go build -o kpx .
Install from a release archive:
tar -xzf kpx_0.1.11_darwin_arm64.tar.gz
install -m 0755 kpx_0.1.11_darwin_arm64/kpx ~/.local/bin/kpx
Choose the archive that matches your platform:
kpx_<version>_darwin_amd64.tar.gzkpx_<version>_darwin_arm64.tar.gzkpx_<version>_linux_amd64.tar.gz
Requirements:
- Go
1.25or newer for source builds - macOS is the primary supported platform today
- Linux release archives are published for
amd64 - Unix-style advisory locking is used for cooperating
kpxprocesses - Non-Unix platforms may compile for development purposes, but real vault operations are not supported there today because advisory file locking is Unix-only in the current implementation
The base release version is stored in internal/buildinfo/VERSION.txt. Builds always take the base version from that file, and append VCS metadata automatically when available.
Shell Completion
Bash completion is shipped as a generated file in completions/kpx.bash.
Install it on macOS with Homebrew Bash completion:
brew install bash-completion@2
mkdir -p "$(brew --prefix)/etc/bash_completion.d"
cp ./completions/kpx.bash "$(brew --prefix)/etc/bash_completion.d/kpx"
Install it on Linux for the current shell:
mkdir -p ~/.local/share/bash-completion/completions
cp ./completions/kpx.bash ~/.local/share/bash-completion/completions/kpx
If you use release archives, extract the matching macOS or Linux tarball and place kpx somewhere on your PATH, for example ~/.local/bin.
Quick Start
Create a vault:
printf '%s\n' 'master-password' | ./kpx --master-password-stdin db create ./vault.kdbx --name "Personal Vault"
Create a group:
printf '%s\n' 'master-password' | ./kpx --master-password-stdin group add ./vault.kdbx /Personal
Add an entry:
printf '%s\n' 'master-password' | ./kpx --master-password-stdin entry add ./vault.kdbx /Personal/GitHub \
--username alice \
--password 'entry-password' \
--url https://github.com \
--notes 'Personal account'
Show an entry:
printf '%s\n' 'master-password' | ./kpx --master-password-stdin entry show ./vault.kdbx /Personal/GitHub
Reveal a password explicitly:
printf '%s\n' 'master-password' | ./kpx --master-password-stdin entry show ./vault.kdbx /Personal/GitHub --reveal
Print only the password for piping to pbcopy:
printf '%s\n' 'master-password' | ./kpx --master-password-stdin entry password ./vault.kdbx /Personal/GitHub | pbcopy
Search by title:
printf '%s\n' 'master-password' | ./kpx --master-password-stdin find ./vault.kdbx github
Create a paper backup:
printf '%s\n' 'master-password' | ./kpx --master-password-stdin export paper ./vault.kdbx \
--output ./vault-paper-backup.txt
Configuration
Create $XDG_CONFIG_HOME/kpx/config.yml or ~/.config/kpx/config.yml:
default_database: /Users/you/vault.kdbx
reveal: false
master_password_cache_seconds: 0
backup_directory: ""
backup_filename_format: "{db_filename}.{timestamp}.{db_ext}"
save_method: "temporary_file"
Set master_password_cache_seconds to a positive number to cache the master password for that many seconds. The default is 0, which disables caching.
On Unix-like systems, kpx follows the XDG base directory convention:
- config:
$XDG_CONFIG_HOME/kpx/config.ymlor~/.config/kpx/config.ymlwhenXDG_CONFIG_HOMEis unset - cache:
$XDG_CACHE_HOME/kpx/master-password-cache.ymlor~/.cache/kpx/master-password-cache.ymlwhenXDG_CACHE_HOMEis unset
For compatibility, kpx moves an existing legacy ~/.kpx/config.yml into the XDG config path the first time it loads config and no XDG config file exists yet.
Leave backup_directory empty to store backups alongside the database. The default filename format uses the original database filename plus a UTC timestamp.
save_method defaults to "temporary_file". Set it to "direct_write" to write directly to the database file instead.
Available backup filename placeholders:
{db_filename}: database filename without the extension{db_stem}{db_ext}{timestamp}
Then omit the database path for vault commands:
printf '%s\n' 'master-password' | ./kpx --master-password-stdin entry show /Personal/GitHub
If reveal: true is set in the config, entry show reveals passwords by default. Passing --reveal on the CLI still takes precedence when you want to override the config for a specific command.
Commands
The CLI follows a simple noun/verb structure:
kpx db ...
kpx group ...
kpx entry ...
kpx export ...
kpx find ...
Version checks:
kpx version
kpx --version
JSON output:
kpx --json entry show ./vault.kdbx /Personal/GitHub
Global flags:
-q,--quiet: suppress success output--json: emit JSON output when supported by the command--no-input: disable interactive prompting--master-password-stdin: read the database master password from stdin
JSON support is available for the commands listed below. For the commands documented here, the top-level JSON response shape is part of the supported CLI contract. Future changes should be additive where possible.
Available today:
kpx db createkpx db validatekpx group lskpx group addkpx entry lskpx entry showkpx entry passwordkpx entry addkpx entry editkpx entry rmkpx export paperkpx findkpx versionkpx --version
Path rules:
- group paths look like
/Personal/Email - entry paths look like
/Personal/GitHub - the database argument is optional when
$XDG_CONFIG_HOME/kpx/config.ymlor~/.config/kpx/config.ymldefinesdefault_database entry showusesrevealfrom config unless--revealis explicitly passed--jsonemits machine-readable output for supported commands using the documented response envelopes below
JSON envelopes by command:
-
db create,db validate,group add,entry add,entry edit,entry rm,export paperReturn a status object withstatus,kind, and command-specific fields such aspath,output, andformat. -
group lsReturns{ "groups": [...] }. -
entry lsReturns{ "group": "...", "entries": [...] }. -
entry showReturns{ "entry": { ... } }. -
entry passwordReturns{ "path": "...", "password": "..." }. -
findReturns{ "query": "...", "exact": false, "results": [...] }. -
version,--versionReturns{ "version": "..." }. -
entry showJSON continues to redact secrets unless--revealis explicitly passed -
--master-password-stdinis the standard flag for reading the database master password from stdin -
--entry-password-stdinis the standard flag for reading an entry password from stdin -
--no-inputdisables interactive prompts and requires stdin flags or cached credentials for secret input -
entry rmrequires--forcewhen--no-inputis set -
ambiguous matches fail closed
Non-interactive examples:
printf '%s\n' 'master-password' | ./kpx --no-input --master-password-stdin db validate ./vault.kdbx
printf '%s\n' 'master-password' | ./kpx --no-input --master-password-stdin entry rm ./vault.kdbx /Personal/GitHub --force
printf '%s\n%s\n' 'master-password' 'entry-password' | ./kpx --no-input --master-password-stdin entry add ./vault.kdbx /Personal/GitLab --entry-password-stdin
Command Reference
db create <database>
- creates a new
KDBX4database - prompts for a master password unless
--master-password-stdinis used - supports
--nameto set the stored database display name
db validate [database]
- verifies that the database can be opened with the supplied password
- uses
default_databasefrom config when the database argument is omitted
group ls [database]
- lists group paths
- uses
default_databasefrom config when the database argument is omitted
group add [database] <group-path>
- creates each missing path segment under the requested group path
- uses
default_databasefrom config when the database argument is omitted
entry ls [database] <group-path>
- lists entries directly under a group
- JSON output includes the group path plus entry paths
entry show [database] <entry-path>
- shows entry details
- redacts passwords by default
- supports
--revealto show the stored password and protected custom fields - uses
revealfrom config unless--revealis explicitly passed
entry password [database] <entry-path>
- prints only the entry password
- useful for piping into other tools
- JSON output includes
pathandpassword
entry add [database] <entry-path>
- creates a new entry at the given path
- supports
--username,--password,--url,--notes - supports
--entry-password-stdinfor reading the entry password from stdin - supports repeated
--field KEY=VALUEfor custom fields
entry edit [database] <entry-path>
- updates built-in and custom fields for an existing entry
- supports
--title,--username,--password,--url,--notes - supports
--entry-password-stdinfor reading the replacement password from stdin - supports repeated
--set-field KEY=VALUE - supports repeated
--clear-field KEY
entry rm [database] <entry-path>
- deletes an entry
- prompts for confirmation unless
--forceis provided - requires
--forcewhen--no-inputis set
find [database] <query>
- searches entry titles and full paths
- supports
--exactfor case-insensitive exact matching instead of substring matching
export paper [database]
- writes a plaintext recovery export for printing or secure offline storage
- requires either
--output <path>or explicit--stdout - supports
--group <group-path>to limit output to a group subtree - supports
--entry <entry-path>to export a single entry --groupand--entryare mutually exclusive
version
- prints the current build version
kpx --versionis also supported
Security Notes
- secrets are redacted by default
- password prompts use the controlling tty and do not echo
- master password caching is disabled by default
- when enabled, cached master passwords are stored on disk under
$XDG_CACHE_HOME/kpx/master-password-cache.ymlor~/.cache/kpx/master-password-cache.ymlwith restrictive file permissions and a cooperating file lock - cooperating
kpxprocesses use a stable adjacent lock file with restrictive permissions to coordinate reads and writes - database saves create a backup of the existing file before replacing it
- save method defaults to temporary-file-then-rename and can be changed to direct write in config
- cooperating
kpxprocesses take advisory locks during database reads and hold an exclusive lock across write operations - non-interactive usage supports stdin-based secrets
- writes are atomic when
save_methodis left at the defaulttemporary_file - destructive entry deletion requires confirmation unless
--forceis provided
Current limitations:
- key files are not supported yet
- advisory locks coordinate cooperating
kpxprocesses, not every external KeePass tool direct_writeis available for compatibility, buttemporary_fileremains the safer default- paper export writes plaintext secrets and should be handled like a physical recovery artifact
Troubleshooting
no interactive tty availableUse--master-password-stdinfor database passwords and--entry-password-stdinfor entry passwords when running non-interactively.interactive input disabled--no-inputturns off prompts entirely. Pair it with stdin flags or a configured master-password cache.delete requires --force when --no-input is setAdd--forcetoentry rmwhen scripting deletes.- advisory locking unsupported on this platform Real vault operations are currently supported on Unix-like systems only.
- release archive command not found after extraction
Move the extracted
kpxbinary into a directory on yourPATH, such as~/.local/bin.
Roadmap
Planned next:
- key file support
- password + key file support
- group rename, move, and delete
- stronger KeePassXC fixture coverage
Compatibility
kpx is currently built around tobischo/gokeepasslib/v3 behind an internal adapter.
The test suite currently includes:
- CLI end-to-end flow tests
- repeated save/open round-trip tests
- atomic write tests
- fixture-driven compatibility tests across both save methods and backup-enabled saves
The compatibility harness includes official on-demand fixtures from the KeePassXC upstream repository, currently covering NewDatabase.kdbx and NonAscii.kdbx from the upstream test suite.
Remote fixtures are opt-in so regular go test ./... runs stay offline. To exercise them explicitly:
KPX_REMOTE_FIXTURES=1 go test ./internal/testcompat -run TestCompatibilityFixtures
For reliability, fixture URLs are pinned to a specific upstream commit and verified with SHA-256 instead of following a floating latest URL.
Development
Development information starts here so the sections above stay focused on end users.
Run the test suite:
GOCACHE=/tmp/gocache go test ./...
Run additional static analysis:
GOCACHE=/tmp/gocache go vet ./...
Create a release:
# 1. Bump the version and update versioned README references in one step
./scripts/bump_version.sh 0.1.11
# 2. Run the test suite
go test ./...
# 3. Create and push the matching tag
git tag v$(tr -d '\n' < internal/buildinfo/VERSION.txt)
git push origin main --tags
./scripts/bump_version.sh updates internal/buildinfo/VERSION.txt, refreshes the versioned install examples in README.md, and runs the release metadata check.
The release metadata check verifies that the current version is aligned across internal/buildinfo/VERSION.txt and the versioned references in README.md.
Pushing a v* tag runs the GitHub release workflow, verifies the tag matches internal/buildinfo/VERSION.txt, reruns ./scripts/check_release.sh --expect-tag, runs go test ./..., and publishes release archives plus checksums.
Release archives are currently published for macOS and Linux targets only. Windows release packaging is out of scope for now.
Project layout:
cmd/ Cobra command definitions
internal/buildinfo/ version metadata exposed to the CLI
internal/cli/ prompting, exit handling, and CLI helpers
internal/config/ optional user config for default database selection
internal/store/ atomic file writes
internal/testcompat/fixture-driven compatibility tests
internal/vault/ KDBX-backed vault adapter
Additional project docs:
Non-Goals
Out of scope for the early versions:
- desktop GUI
- browser integration
- desktop integration
- SSH agent integration
- auto-type
- sync and merge
- attachments and entry history
KDBX3support
License
MIT. See LICENSE.
Documentation
¶
There is no documentation for this package.