⚠️ Alpha software. Do not use with a real Vaultwarden vault yet. The cryptography has not been independently audited, no stable release has shipped, and a significant portion of the code was produced with AI assistance under human review. See DISCLAIMER.md for the full picture before you clone.
A modern desktop client for Vaultwarden and Bitwarden.
Clavix is an alternative to the official Bitwarden client and Keyguard, built for the self-hosted Vaultwarden community. The goal: finally provide a comfortable tree-based vault with drag & drop, the way KeePassXC has offered for years.
Status: alpha, read+write. Releases starting from
v0.1.7support creating and editing items, full 2FA (including WebAuthn and YubiKey OTP), an embedded SSH agent, KeePassXC import and a built-in security audit. See CHANGELOG.md for the full picture per version.
Against a real Vaultwarden instance, Clavix:
~/.local/share/clavix/ —
on restart you land on an Unlock screen that only asks for the
master password, the OAuth2 token refreshes automatically 60 s
before expiry, and the refresh token stored on disk is itself
encrypted under the user key;/ciphers/create endpoint;otpauth:// URIs with custom period, digits, or hash algorithm);jsQR + getUserMedia);/ naming convention (personal folders + collections
per organization), with a draggable splitter to resize the
tree panel (width persisted to localStorage);ssh, git,
scp, rsync, … can use them without writing the private keys
to disk, the same way Bitwarden Desktop now does.The master password never hits the server nor the disk: only derived
values are exchanged (master password hash for authentication, master
key for local decryption). Every sensitive key (MasterKey,
SymmetricKey) derives ZeroizeOnDrop to wipe its memory on
destruction.
The official Bitwarden client (Electron) has a dated UX and does not offer real tree-drag-and-drop. Keyguard, the most serious alternative, is read-only without a premium subscription and handles deep hierarchies poorly. Clavix aims to fill that gap for the self-hosted community.
~/.local/share/clavix/ with
0600 permissions (cross-platform via the dirs crate)rusqlite bundled) with the whole
SyncResponse encrypted by the user key before being storedClavix does not use the official Bitwarden SDK (ambiguous license). The crypto is reimplemented in-project, under GPL-3.0.
/-separated names into a hierarchy/ciphers/{id}/partial)/ciphers/{id}/share, re-encrypted client-side with the
target org key)data.message from the
Vaultwarden API is still returned as-is).Attachments, Sends, passkeys (storing them in the vault), browser autofill.
sudo apt install \
libwebkit2gtk-4.1-dev \
libjavascriptcoregtk-4.1-dev \
libsoup-3.0-dev \
libxdo-dev \
libssl-dev \
librsvg2-dev \
libayatana-appindicator3-dev \
libudev-dev \
build-essential pkg-config curl wget file
libudev-devis pulled in byhidapi(used by the FIDO2 WebAuthn path). Without it,cargo builderrors out on thehidapisys crate.
See the Tauri prerequisites.
pnpm install
pnpm tauri dev
The first Rust compilation takes a few minutes (full Tauri dependency
graph). Subsequent compiles are incremental thanks to the target/
cache.
The WebdriverIO suite (pnpm test:e2e) drives the real Tauri binary
against a disposable Vaultwarden container. On top of the base
requirements above, you need:
tauri-driver — installed via Cargo into ~/.cargo/bin/:
cargo install tauri-driver --locked
WebKitWebDriver + a virtual display on Linux:
sudo apt install webkit2gtk-driver xvfb
Docker + Docker Compose plugin — the suite boots a
Vaultwarden container from tests/e2e/docker-compose.yml and tears
it down on exit. Set E2E_SKIP_DOCKER=1 to reuse an
externally-managed instance.
Then build the debug binary once and run the suite under xvfb-run:
pnpm tauri build --debug --no-bundle
xvfb-run -a pnpm test:e2e
clavix/
├── src-tauri/ Rust backend (Tauri)
│ └── src/
│ ├── main.rs Binary entry point
│ ├── lib.rs Tauri setup + command registry
│ ├── commands/ Tauri commands, one module per domain
│ │ (auth, vault, cipher, move_share, ssh,
│ │ audit)
│ ├── services/ Internal helpers used by commands (auth
│ │ token refresh, cipher body builder,
│ │ vault summary projection)
│ ├── api.rs Vaultwarden HTTP client
│ ├── crypto.rs Key derivation, EncString (AES / RSA),
│ │ encrypt / re-encrypt for server updates
│ ├── audit.rs HIBP k-anonymity + reused/weak detection
│ ├── ssh_agent.rs Unix-socket SSH agent (Ed25519 + RSA)
│ ├── webauthn.rs CTAP2 / HID WebAuthn path for 2FA
│ ├── models.rs API types and DTOs sent to the UI
│ ├── state.rs AppState (session, ssh agent handle,
│ │ auto-lock watchdog timestamps)
│ ├── store.rs On-disk session persistence
│ ├── cache.rs Encrypted SQLite vault cache + op-log
│ └── error.rs Unified Error type, serialized as
│ { code, message, data }
├── src/ SvelteKit frontend (static output, no SSR)
│ ├── app.html
│ ├── lib/
│ │ ├── *.svelte One component per UI area (AuthGate,
│ │ │ VaultSidebar, CipherList, CipherDetail,
│ │ │ CipherEditor, ImportDialog, QrScanner,
│ │ │ TotpField, …)
│ │ ├── *.svelte.ts Runes-based controllers
│ │ │ (auth, vault, prefs, clipboard, drag)
│ │ ├── api.ts Typed wrappers around Tauri commands
│ │ ├── types.ts Shared TS types
│ │ ├── totp.ts RFC 6238 TOTP generator (Web Crypto)
│ │ ├── csv.ts KeePassXC CSV parser
│ │ └── paraglide/ Compiled i18n (gitignored)
│ └── routes/
│ ├── +layout.svelte Global styles + locale bootstrap
│ └── +page.svelte Orchestrates the controllers + layout
├── messages/{fr,en}.json i18n source strings (paraglide-js)
├── .github/workflows/
│ ├── ci.yml fmt + clippy + audit + svelte-check +
│ │ vitest + cargo test
│ ├── codeql.yml CodeQL scan
│ └── release.yml Multi-OS release bundles
└── CHANGELOG.md Per-version notes
MasterKey,
SymmetricKey) derive ZeroizeOnDrop: their memory is wiped on
scope exit. The password travels through SecretString (secrecy
crate), which prevents Debug from leaking it into logs.hmac::Mac::verify_slice) before decryption.~/.local/share/clavix/session.json
(0600 permissions on Unix). Sensitive fields (Key, PrivateKey)
stay encrypted by the stretched master key, and the OAuth2 refresh
token is itself encrypted under the user key. Clavix still assumes
your user disk is under your control; full-disk encryption such as
LUKS is recommended.Vulnerabilities should be reported privately to the maintainer before any public disclosure.
Every push and pull request against master triggers a GitHub
Actions workflow that runs:
cargo fmt --check — Rust style.cargo clippy --all-targets -- -D warnings — strict lint.cargo test — Rust unit tests on crypto, audit, ssh agent,
webauthn challenge parsing, cipher body builder, and the API
helpers (extract_*, normalize_base_url).cargo audit — vulnerability scan on dependencies
(RUSTSEC-2023-0071 on the rsa crate is ignored, see the comment
in .github/workflows/ci.yml).pnpm check (svelte-check) — TypeScript / Svelte typing.pnpm test (vitest) — unit tests on the pure TS helpers (tree,
filter, drag, format, generator, CSV).javascript-typescript) on a separate workflow.Tauri system libraries are installed on every run; the target/
cache is handled by Swatinem/rust-cache.
See CONTRIBUTING.md for the setup, code style,
and PR conventions. Security-sensitive changes (crypto.rs,
webauthn.rs, ssh_agent.rs, session storage) should come with
matching tests.