Diff/Merge keepassxc databases
  • C++ 82.7%
  • CMake 9.3%
  • Nix 8%
Find a file
2026-08-23 22:51:33 +05:30
docs/assets Add KDBX merge utility 2026-08-23 22:51:33 +05:30
src Add KDBX merge utility 2026-08-23 22:51:33 +05:30
tests Add KDBX merge utility 2026-08-23 22:51:33 +05:30
.clang-format Add KDBX merge utility 2026-08-23 22:51:33 +05:30
.clang-tidy Add KDBX merge utility 2026-08-23 22:51:33 +05:30
.clangd Add KDBX merge utility 2026-08-23 22:51:33 +05:30
.gitignore Add KDBX merge utility 2026-08-23 22:51:33 +05:30
CMakeLists.txt Add KDBX merge utility 2026-08-23 22:51:33 +05:30
CMakePresets.json Add KDBX merge utility 2026-08-23 22:51:33 +05:30
flake.lock Add KDBX merge utility 2026-08-23 22:51:33 +05:30
flake.nix Add KDBX merge utility 2026-08-23 22:51:33 +05:30
keepassxc-core.nix Add KDBX merge utility 2026-08-23 22:51:33 +05:30
package.nix Add KDBX merge utility 2026-08-23 22:51:33 +05:30
README.md Add KDBX merge utility 2026-08-23 22:51:33 +05:30
treefmt.toml Add KDBX merge utility 2026-08-23 22:51:33 +05:30

kdbx-merge

kdbx-merge reviews KeePassXC KDBX databases field by field and writes a new database after explicit conflict resolution. Input databases are never modified.

KeePassXC 2.7.12 is built once as the separate keepassxc-core Nix package. The application links against that package instead of rebuilding KeePassXC when application sources change.

Demo

Watch the interactive merge walkthrough Interactive KDBX merge walkthrough

Play the interactive terminal cast on asciinema.

Development

Enter the reproducible development shell and configure the persistent local build directory:

nix develop
cmake --preset dev
cmake --build --preset dev
ctest --preset dev

The build/ directory is intentionally local and ignored. Ninja recompiles only changed files.

Format C++ sources and verify formatting with the same toolchain used by CI:

nix fmt
cmake --build --preset dev --target kdbx-merge-format-check

The developer shell includes Clang, clangd, clang-format, clang-tidy, cppcheck, include-what-you-use, doctest, Valgrind, and Cachegrind.

Verification Profiles

Run AddressSanitizer and UndefinedBehaviorSanitizer tests:

nix develop .#asan
cmake --preset asan-ubsan
cmake --build --preset asan-ubsan
ctest --preset asan-ubsan

Run Valgrind Memcheck and Cachegrind:

nix develop
cmake --preset valgrind
cmake --build --preset valgrind
ctest --preset valgrind
cmake --build --preset valgrind --target kdbx-merge-cachegrind
cg_annotate build-valgrind/cachegrind.out

Run optional static analyzers in isolated build directories:

nix develop .#tidy
cmake --preset clang-tidy
cmake --build --preset clang-tidy

cmake --build --preset dev --target kdbx-merge-cppcheck

cmake --preset iwyu
cmake --build --preset iwyu

-Werror is available when a clean warning gate is desired:

cmake -S . -B build-werror -G Ninja \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_CXX_COMPILER=clang++ \
  -DKDBX_MERGE_BUILD_TESTS=ON \
  -DKDBX_MERGE_ENABLE_WERROR=ON
cmake --build build-werror

Neovim and clangd

Configure the dev preset once from inside nix develop. .clangd points at the resulting build/compile_commands.json. The development shell exports CLANGD_FLAGS with the Nix Clang compiler as --query-driver, so Neovim can use the real clangd binary directly:

cmd = { "clangd" }

Start Neovim from the development shell so clangd and the wrapped compiler are on PATH. The shell exports CLANGD_FLAGS, so the real clangd command used by Neovim can query the Nix compiler directly.

Database Workflow

Close KeePassXC and pause Syncthing before reviewing or merging. Never use an input path as the output path.

Review the canonical database against each conflict copy:

./build/kdbx-merge diff \
  /path/to/canonical.kdbx \
  /path/to/conflict-copy.kdbx \
  --same-credentials

The password is requested through /dev/tty; it is not accepted on the command line. Protected values are masked unless --show-sensitive is used.

Write a new authoritative database after resolving each field:

./build/kdbx-merge merge \
  --target /path/to/canonical.kdbx \
  --source /path/to/conflict-copy-1.kdbx \
  --source /path/to/conflict-copy-2.kdbx \
  --output /path/to/merged-output.kdbx \
  --same-credentials

Choose Finish later in the resolver to save an owner-only checkpoint beside the output (for example, merged-output.kdbx.checkpoint). Continue with the same command plus --resume; use --checkpoint PATH to place the sidecar elsewhere. The checkpoint records only input paths, encrypted-file hashes, and field choices—not passwords or field values—but those paths and field labels are still sensitive metadata. It is removed after a successful save or an explicit cancellation.

Inspect the resulting file in KeePassXC before making it canonical. The tool uses an atomic output write and reopens the output to verify it. Once the result is confirmed, close KeePassXC, replace the canonical file with the verified output, preserve the old file as a backup, and resume Syncthing.