An end-to-end encrypted password manager that syncs over Matrix.
Your vault is a private encrypted room. Your server is any Matrix homeserver.
Vaultrix stores your passwords as an append-only, end-to-end encrypted event log in a private Matrix room. There is no Vaultrix server and no account to create with us: any Matrix homeserver, including one you run yourself, is the sync backend, and it only ever sees ciphertext.
Matrix already solved the hard problems of a password manager backend, so Vaultrix leans on the real thing instead of reinventing it:
- Item history and undelete. The room timeline is an append-only version log. Every change to every entry is recoverable by design, a feature other managers paywall.
- Multi-device sync. Devices converge through ordinary Matrix sync; a live timeline watcher applies changes from your other devices as they arrive.
- Recovery. The standard Matrix recovery key (SSSS) is the vault's "secret key". One key, printable on an emergency kit, unlocks everything on a fresh device.
- Shared vaults. Rooms, invites, and power levels are exactly the substrate a shared family or team vault needs (on the roadmap).
- Self-hosting. Run Synapse or any other homeserver and you own the whole stack.
- One vault per private Megolm-encrypted room, with all vault semantics client-side
- Double encryption: entries are AES-256-GCM encrypted under a vault master key before Megolm ever sees them
- Fresh-device unlock with a standard Matrix recovery key, restoring from mandatory server-side key backup
- TOTP and HOTP code generation, tested against the RFC vectors
- Conflict resolution that can never resurrect a deleted entry (per-entry last-write-wins with tombstones)
- Key rotation with vault epochs, snapshot compaction for fast cold starts
- Vault export and recovery-key QR codes
- One core, three frontends: MV3 browser extension, web UI, and Tauri desktop app
Alpha, under active development. The Matrix E2EE layer is the standard audited stack (matrix-js-sdk with vodozemac), but Vaultrix itself has not had an independent security review. Do not make it the only copy of credentials you care about yet.
flowchart LR
subgraph device["Your device"]
OP["Vault op<br/>(create / update / delete)"] -->|"AES-256-GCM under K_vault"| APP["App-layer ciphertext"]
APP -->|"Megolm room encryption"| EV["Matrix event"]
end
EV --> HS[("Homeserver<br/>ciphertext only")]
SSSS["Secret storage (SSSS)<br/>holds K_vault + backup key"] -.->|"recovery key"| OP
- One vault = one private Megolm-encrypted Matrix room. Entries live as an append-only log of custom
com.vaultrix.vault.v1.opevents, with periodic...snapshotevents for compaction. - Double encryption. Each op/snapshot payload is encrypted app-side with a 32-byte vault master key
K_vault(AES-256-GCM), then the event itself is Megolm-encrypted by the room like any Matrix message. K_vaultlives in Matrix secret storage (SSSS), protected by the standard Matrix recovery key created when the vault is provisioned.- Key backup is mandatory for vaults. Provisioning enables server-side key backup; a fresh device unlocks by loading the backup key from SSSS and restoring Megolm keys, then back-paginating the room to the start and replaying snapshot + ops. This is the canonical matrix-js-sdk flow (E2EE docs, vodozemac via
initRustCrypto()). - Conflict resolution is last-write-wins per entry with tombstones: the (ts, op_id) of the last applied op is tracked per entry id, so a stale update can never resurrect a deleted entry.
- Key rotation bumps the vault epoch: new
K_vaultin SSSS, fresh snapshot encrypted with the new key; ops from older epochs are ignored.
Unlock flow on a fresh device:
- Password login, then
initRustCrypto()and an initial sync - Recovery key opens SSSS, yielding
K_vault - Megolm keys restore from server-side key backup
- The vault room is back-paginated fully, decrypted, latest snapshot applied, later ops replayed
- A live timeline watcher applies ops from other devices as they arrive
The homeserver stores only doubly-encrypted payloads. It does see metadata: which user talks to which rooms, event timestamps and sizes, and your device list. It never sees entry contents, titles, URLs, or K_vault. Even a future Megolm key compromise alone is not enough to read entries, since payloads are still AES-GCM encrypted under K_vault.
Prerequisites: Node >= 18, pnpm 9, and Docker if you want to run the integration or e2e suites.
pnpm install
pnpm run build # all packages
pnpm run build:extension # core + ui + extension bundleBuild, then open chrome://extensions, enable "Developer mode", choose "Load unpacked", and select extension/dist. In the popup, "Open vault" opens the options page with the full unlock and vault UI.
pnpm run dev:uipnpm run dev:tauri # development
pnpm run build:tauri # release buildBoth scripts regenerate tauri/icons/icon.png from assets/ via pnpm run ensure-tauri-icon (the file is required by Tauri's generate_context!() and is not checked in).
pnpm run test # core unit tests (crypto, LWW model, TOTP RFC vectors, ...)
pnpm run test:integration # full vault lifecycle against a throwaway Synapse (needs Docker)
pnpm run test:e2e # Playwright browser e2e of the web UI (needs Docker)
pnpm run test:allThe integration suite spins up a disposable Synapse container and exercises the real thing end to end: provisioning (SSSS + key backup + room + K_vault), writing entries past the sync-window size, fresh-device unlock via recovery key, live multi-device sync, delete/LWW semantics, snapshot compaction, and key rotation.
| Path | What lives there |
|---|---|
packages/core |
Matrix client wrapper, vault room, SSSS + key backup, vault model (LWW + tombstones), op log, snapshots, session (unlock + watch), provisioning, rotation, export, recovery QR, TOTP/HOTP |
packages/ui |
React app: unlock screen, vault list. The same bundle serves the extension options page and the Tauri app |
extension |
MV3 browser extension: background service worker (Matrix + vault), popup, content script (autofill), options page |
tauri |
Tauri 2 desktop shell around the UI bundle, with Rust capabilities (clipboard, shell-open) |
assets |
Logo and icon sources (SVG masters plus rendered PNGs) |
The durable spec and phased roadmap live in SPEC.md. The current focus is the browser extension: inline autofill, save/update prompts, a password generator, and TOTP fill.
Issues and pull requests are welcome. Before submitting, please run pnpm run test; run the integration suite too if you touched anything in packages/core. Keep changes scoped and include tests for new vault semantics.