No description
  • Rust 96.5%
  • Python 1.3%
  • CSS 1.1%
  • Shell 0.7%
  • Meson 0.4%
Find a file
salix 1c3930215d
All checks were successful
Publish x86_64 Flatpak Nightly / flatpak (push) Successful in 2m18s
Load protected custom fields in the entry editor
- Fetch protected field values before opening the editor
- Hide the empty custom fields list and update protection tooltips
2026-10-16 23:32:50 +02:00
.forgejo/workflows refactor: rename Salix to Armoire 2026-09-04 01:57:12 +02:00
build-aux perf(keepass): use flate2's zlib-rs backend 2026-09-30 12:27:18 +02:00
crates Load protected custom fields in the entry editor 2026-10-16 23:32:50 +02:00
data Merge branch 'worktree-agent-a5f7b119a319e872e' into ui-polish 2026-10-06 01:32:45 +02:00
docs/plans docs(plans): add team databases sharing plan 2026-10-06 12:18:14 +02:00
scripts feat(backups): preview and restore encrypted backups as new databases 2026-09-16 12:08:00 +02:00
vendor/keepass docs(keepass): refresh patch notes for zlib-rs, XML and seed changes 2026-09-30 12:27:42 +02:00
.gitignore chore: ignore the design folder 2026-09-04 14:00:38 +02:00
Cargo.lock feat(security): add PIN quick unlock for locked vaults 2026-10-02 13:57:30 +02:00
Cargo.toml fix(sync): rescue surviving entries when a group deletion wins 2026-09-30 09:35:07 +02:00
meson.build fix(build): sync meson blueprint and depend files with the ui sources 2026-09-04 11:02:46 +02:00
meson_options.txt refactor: rename Salix to Armoire 2026-09-04 01:57:12 +02:00
README.md feat(ui): polish generator, strength meter, breach check, favicons and sync conflicts 2026-10-06 00:53:44 +02:00
rustfmt.toml Scaffold Salix GTK application 2026-09-02 13:46:16 +02:00

Armoire

GTK4 / libadwaita KeePass client. Open and create KDBX 4 databases, browse groups and entries, copy credentials with a timed clipboard clear, and show time-based codes in the Authenticator view.

Use the main menu to change unlock credentials, rename the selected group, manage multiple entries, or empty Deleted Entries. Entry menus include Duplicate Entry and permanent deletion for recycled entries. The bulk dialog acts on entries in the current view and supports move, recycle, restore, and permanent deletion. Attachment removal stays in the entry draft until Save; Discard keeps the stored attachments.

The Weak Passwords filter evaluates nonempty passwords; notes and other entries without passwords are excluded. The Reused Passwords sidebar filter finds nonempty passwords shared by live entries. Checks stay inside the unlocked vault; deleted entries and password history are excluded.

Each entry with a password offers Check for password breaches…, an optional Have I Been Pwned check. After you choose Check Password, Armoire hashes that password locally and sends only the first five hex characters of its SHA-1 hash over HTTPS to the Pwned Passwords range API. The full hash, password, username and website are never sent. Matching happens locally, with response padding enabled and redirects disabled. HIBP can see your IP address and the hash prefix. Checks do not run automatically or while typing; results are temporary and “not found” does not guarantee a password is safe.

Use Check All Passwords for Breaches… in the main menu to check every live entry with a saved password, regardless of the current search or sidebar filter. Deleted entries and history are excluded. Shared hash prefixes use one request; the summary lists breached entries and failures separately, and activating a breached entry opens it. If Have I Been Pwned cannot be reached, the check stops after the first failed request. Stop or lock the database to stop further requests (an in-flight request may finish). Results refer to the saved passwords read when the check ran and are not persisted.

Search accepts tag:work, group:"Personal Accounts", title:mail, user:alice, url:example.com, has:totp, has:password, has:attachment, has:history, is:expired, is:expiring, is:favourite, is:weak, and is:reused. Combine filters with ordinary search terms or prefix a filter with - to exclude it. Expiring means the next 30 days, excluding already expired entries. Use the save button beside Saved Searches to keep the current query; each saved query can be opened or removed from the sidebar. Queries are stored locally in plain text in application settings, shared across databases. Searches saved from Deleted Entries retain that scope.

Entry History compares saved and current field values, masked until revealed, and lists other changed settings. Restore This Version preserves the replaced version in history and keeps the entry in its current group.

New from Template in the main menu offers Login, Secure Note, API Token, and Server. Templates are editable drafts backed by ordinary KeePass fields. Secure Note uses the encrypted Notes field; API Token stores the token in Password.

Recover Backup is available from the main menu even before opening a database, and from Preferences. It lists local backups by date, offers an optional unlock preview with entry/group/attachment counts, and restores an encrypted copy to a new local filename. Recovery never replaces an existing file. Preview and restored copies need the password and key file used when that backup was created.

Preferences shows the database path with copy and open-folder buttons. Its Security page opens the credential editor, where password and key-file components can each be kept, replaced or removed. At least one component is required. Credential changes save immediately, even with autosave off, and adopt the new credentials only after the file write succeeds. Backups retain their old keys. Connected WebDAV copies are re-encrypted on the next successful sync. Finish that sync before locking: old credentials for the transition are held only while the vault stays unlocked. If sync fails and the vault is locked, the remote copy still needs its previous credentials.

Authenticator and the entry editor can import a TOTP QR code from a PNG or JPEG image. Decoding happens locally; imported values remain editable before Save. Images are limited to 8 MiB, 4096 pixels per side and 4 megapixels. Each image must contain exactly one supported TOTP code. HOTP, migration bundles and camera capture are not supported.

Application IDs:

  • production: host.salix.Armoire
  • development: host.salix.Armoire.Devel

The executable is armoire in both cases. cargo run uses the development ID unless ARMOIRE_APP_ID is set.

Prerequisites

Native builds need:

  • Rust 1.92 or newer (rustc, cargo)
  • GTK 4.22 and libadwaita 1.9 (GNOME 50)
  • glib-compile-resources and glib-compile-schemas
  • blueprint-compiler >= 0.14 (cargo and Meson both compile the nine .blp files)

Flatpak builds need:

  • flatpak and flatpak-builder
  • org.gnome.Platform//50
  • org.gnome.Sdk//50
  • org.freedesktop.Sdk.Extension.rust-stable//25.08

Install those runtimes from Flathub:

flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50
flatpak install flathub org.freedesktop.Sdk.Extension.rust-stable//25.08

Native: Cargo

From the repository root. This uses the development application ID host.salix.Armoire.Devel.

cargo fmt --check
cargo check --workspace --locked
cargo test --workspace --locked
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo run -p armoire

The GTK workflow, clipboard, and desktop-lock tests need an isolated X11 display and session bus. Install Xvfb, xdotool, and a working desktop file-picker portal, then run each test in its own process (GTK initialization is thread-bound):

xvfb-run -a -s '-screen 0 1600x1200x24' bash scripts/test-ui.sh

Recovery tests drive the actual native file pickers, including cancel, key files, overwrite refusal, and reopening a restored database. Set ARMOIRE_REVIEW_DIR to a directory to capture review screenshots (also requires ImageMagick's import command); ARMOIRE_XDOTOOL can specify an alternate xdotool executable.

To check only the GTK-free crate:

cargo test -p armoire-core --locked

Native: Meson

Development ID:

meson setup build-devel -Dprofile=development
meson compile -C build-devel
meson devenv -C build-devel ./armoire

Production ID, then install to a prefix:

meson setup build-release -Dprofile=default --prefix=/usr
meson compile -C build-release
meson install -C build-release
armoire

Meson compiles the nine Blueprint templates to GtkBuilder XML, compiles GResources, installs the GSettings schema, desktop file, AppStream metainfo, shared-mime-info package for .kdbx, D-Bus service, and icons, then builds armoire with Cargo.

Flatpak

Regenerate vendored crate metadata after Cargo.lock changes:

python3 build-aux/generate-cargo-sources.py

Development build, install, and run:

flatpak-builder --user --install --force-clean _build build-aux/host.salix.Armoire.Devel.json
flatpak run host.salix.Armoire.Devel

Production:

flatpak-builder --user --install --force-clean _build build-aux/host.salix.Armoire.json
flatpak run host.salix.Armoire

The manifests target GNOME 50. Runtime permissions are IPC, Wayland, fallback X11, DRI, logind lock/suspend signals, and network (WebDAV replicas). File and URL access uses portals. Crate sources come from build-aux/cargo-sources.json so the module build can run with CARGO_NET_OFFLINE=true.

Locking discards the decrypted vault. By default, unlocking again requires the password and/or key file. With Quick unlock with a PIN (Preferences → Security), a lock instead seals the credentials, key file included, to a public key derived from the PIN. The sealed copy stays in memory only, and the PIN reopens it. Settings store that public key, never the PIN or the credentials, and it cannot open a database. The master password is required again after the app restarts, after Close Database or unlocking another database, after three wrong PINs, and when the file on disk no longer accepts the sealed credentials. Someone holding both the settings and a memory image could guess a short PIN offline (one Argon2id derivation per guess), so do not reuse a PIN from elsewhere. Credential changes made in Armoire are sealed at the next lock. Previous credentials for a pending WebDAV transition are not sealed. Turning off immediate clipboard clearing on lock still preserves the configured clipboard expiry timer.

WebDAV requests run separately from the decrypted vault worker, so copying, revealing and editing stay responsive during a slow download or upload. A sync saves the merged vault locally before its upload starts. Lock and shutdown stop waiting for network I/O; a request already sent may still finish. A new request is refused while a previous request is still running. WebDAV operations have a 30-second deadline and downloads are limited to 128 MiB. Redirects are refused, so configure the final file URL directly. Creating a remote file uses If-None-Match: *; replacing one requires a strong ETag and If-Match. Sync refuses to replace existing files on servers without strong ETags. A failed precondition preserves the remote file; the next sync downloads it again.

Database processing rejects KDF parameters above these limits before derivation: 10 million AES rounds; 512 MiB Argon2 memory, 100 iterations, 16 lanes, and a memory-times-iterations budget of 2 GiB. Decompressed KDBX payloads are limited to 256 MiB. These limits also apply when opening local files, including cached WebDAV copies. Vaults exceeding them are rejected without reducing their KDF settings. Three-way merges use the changed side or the explicit conflict choice for entry contents and moves, independent of device clock differences.

Favicon downloads are limited to 1 MiB, source images to 4096 pixels per side and 4 megapixels in total, and the stored icon to 128 pixels per side.

KeePassXC compatibility is covered by crates/armoire-core/tests/compatibility.rs. Run the synthetic probes with cargo run -p armoire-core --example compatibility_audit --locked.

Layout

  • crates/armoire-core — KeePass crate integration, no GTK
  • crates/armoire — GTK4 / libadwaita application
  • data/ — desktop, AppStream, GSettings, MIME, icons
  • build-aux/ — Flatpak manifests and cargo-sources generator