A quantum-resistant password manager.
Status: Alpha. All vault operations implemented and tested in Rust; Flutter UI complete.
Gabbro is a free, open-source password manager designed for users who take security seriously. Your secrets are protected by memory-hard key derivation (Argon2id) and AES-256 encryption — both resistant to classical and quantum attack.
Named after the intrusive igneous rock — hard, stable, enduring.
- Quantum-resistant by design — vault security rests on Argon2id + AES-256-GCM, both quantum-resistant. Vaults derive the vault key straight from Argon2id (VERSION 11)
- Hardware key (optional, recommended) — FIDO2/YubiKey authentication; passphrase-only by default, with a minimum of two keys when keys are used (primary + backup)
- Rust for all keys — every cryptographic operation lives in Rust; keys never cross the Flutter/Rust bridge. Secrets you view, generate or autofill do reach Flutter in plaintext to be displayed
- Local-first — your vault lives on your device; sync is your choice and your responsibility (for example with syncthing)
- Localised — UI available in many languages (EN, FR, DE, IT, ES, and more); follows system locale with in-app override
- Multi-language passphrase generator — wordlist library covering many languages; classic generator uses language-native character pools (Greek, Cyrillic, Hiragana/Katakana, Hangul, CJK)
- In-app help — offline help carousel; no external website or internet connection required
- FOSS — GPL-3.0-only licensed
![]() |
![]() |
![]() |
| Your vault | Passphrase generator | Password breakdown |
| Layer | Technology |
|---|---|
| UI | Flutter (Dart) |
| Crypto & secrets | Rust |
| Bridge | flutter_rust_bridge v2 (FFI) |
The Flutter:Rust split follows a strict principle: if it touches a key, it lives in Rust. Everything else lives in Flutter.
| Platform | Target |
|---|---|
| Linux (Arch, Mint) | v1 |
| Android (incl. GrapheneOS) | v1 |
| Windows | v2 (future) |
A plain-language overview. For the version-accurate detail, see the full technical diagram.
passphrase + random_salt
→ Argon2id (KDF)
→ HKDF-SHA256 (vault key; optional YubiKey factor)
→ AES-256-GCM (vault encryption)
→ encrypted vault body + auth tag
Quantum resistance comes from Argon2id + AES-256-GCM. Vaults (VERSION 11) derive the vault key directly from Argon2id. VERSION 11 is the oldest format this build opens — an older vault is refused without being modified, and can be upgraded via docs/VAULT_UPGRADE_PATH.md.
Vault files use the .gabbro extension and are self-contained —
all parameters needed for decryption travel with the file.
Exports include a detached SHA-256 hash for integrity verification.
Every vault export produces two files:
vault.gabbro — the encrypted vault
vault.gabbro.sha256 — detached SHA-256 hash
To verify the export has not been corrupted in transit or storage:
sha256sum -c vault.gabbro.sha256A clean result prints vault.gabbro: OK. This follows the same
convention as Linux ISO verification and can be run before decryption
using any standard tool — no Gabbro installation required.
Note: the detached hash detects accidental corruption, not tampering — anyone who alters the file can recompute it. Tamper detection is AES-256-GCM's authentication tag, checked during decryption. The hash is a UX complement that allows a corruption check before opening the vault.
Gabbro has no server and phones nowhere. The one network call in the entire
source is the Android app-passkey check: with the App passkeys toggle on
(Settings, off by default), a passkey login from a native app fetches that
site's own https:// to verify the app —
nothing else, ever. Linux builds contain no network code at all. Links you
tap open in your browser, not through Gabbro.
Check it yourself:
-
Source. Clone the repo, then scan it (plain grep, preinstalled everywhere):
git clone https://github.com/gabbro-foss/gabbro.git && cd gabbro && grep -rn "openConnection\|HttpClient\|reqwest\|TcpStream" lib/ rust/src/ android/app/src/main
Expected output — exactly one line, the assetlinks fetch:
android/app/src/main/kotlin/app/gabbro/gabbro/GabbroPasskeyActivity.kt:119: val conn = java.net.URL(url).openConnection() as HttpsURLConnection -
On the wire. Capture Gabbro's traffic with PCAPdroid (Android) or Wireshark (Linux). Toggle off: zero packets, always. Toggle on, idle: zero. Toggle on, app-passkey login: exactly one HTTPS request, to the login's own site.
-
Deny and see. Block Gabbro's network with NetGuard or GrapheneOS's Network permission: everything keeps working except app passkeys, which refuse.
Alpha release — the cryptographic implementation
rust/src/cryptohas not yet undergone external review. It is provided as-is, without warranty, as stated in the GPL-3.0.
Release files are on the Releases page. Pick your platform below.
Two ways to install: a native package (recommended — it adds a menu entry and
a gabbro command, pulls in the dependencies, and is removed cleanly by your
package manager), or the portable tarball (any distribution, no root, run from
a folder). All builds require glibc ≥ 2.34, satisfied by all current Arch, Debian
stable, and Mint installations.
yay -S gabbro-bin # or: paru -S gabbro-binInstalls system-wide to /usr with a menu entry and the gabbro command on your
PATH; your AUR helper resolves the dependencies and updates it with the rest of
your system. gabbro-bin repackages the official release build — it does not
recompile from source.
Add the repo once; every later release then arrives through apt upgrade and
Mint's Update Manager. Install the signing key, add the source, install:
sudo install -d /etc/apt/keyrings && sudo curl -fsSL https://gabbro-foss.github.io/gabbro-apt/gabbro-archive-keyring.gpg -o /etc/apt/keyrings/gabbro.gpgsudo tee /etc/apt/sources.list.d/gabbro.sources >/dev/null <<'EOF'
Types: deb
URIs: https://gabbro-foss.github.io/gabbro-apt
Suites: stable
Components: main
Architectures: amd64
Signed-By: /etc/apt/keyrings/gabbro.gpg
EOFsudo apt update && sudo apt install gabbroInstalls system-wide to /usr with a menu entry and the gabbro command; apt
resolves the dependencies.
One-off alternative (no auto-update): download gabbro_ from
the Releases page, then sudo apt install ./gabbro_. It
upgrades in place when you install a newer one.
tar -xzf gabbro-<version>-linux-x86_64.tar.gz
./bundle/gabbroSelf-contained: place bundle/ anywhere and run it in place. No root — but also no
menu entry and no gabbro on your PATH; that system integration is what the
packages add. Nothing resolves dependencies for you either — install the runtime
libraries the packages would have pulled in:
- Arch:
sudo pacman -S --needed libfido2 libcbor pcsclite gtk3 xdg-desktop-portal xdg-desktop-portal-gtk - Debian / Mint:
sudo apt install libfido2-1 libcbor0.10 libpcsclite1 libgtk-3-0t64 xdg-desktop-portal xdg-desktop-portal-gtk
Website passkeys need /dev/uhid access; the AUR and APT packages set this up
for you, the tarball does not. Without it, passkeys silently do nothing while
everything else works. One-time setup (skip if you don't use passkeys):
echo 'KERNEL=="uhid", SUBSYSTEM=="misc", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/70-gabbro-uhid.rules
echo 'uhid' | sudo tee /etc/modules-load.d/gabbro-uhid.conf
sudo udevadm control --reload && sudo modprobe uhid && sudo udevadm trigger --name-match=uhid| Installed via | Remove with |
|---|---|
| AUR | sudo pacman -Rns gabbro-bin |
APT / .deb |
sudo apt remove gabbro |
| tarball | delete the bundle/ folder |
If you added the APT repo, also delete /etc/apt/sources.list.d/gabbro.sources
and /etc/apt/keyrings/gabbro.gpg. If you did the tarball passkey setup,
remove it too (optionally sudo modprobe -r uhid to unload the module until
reboot):
sudo rm /etc/udev/rules.d/70-gabbro-uhid.rules /etc/modules-load.d/gabbro-uhid.conf && sudo udevadm control --reloadIf you bound a custom shortcut for auto-type, unbind it — it would now point at a deleted binary and silently do nothing.
Your vaults and settings are not removed — they live in
~/.local/share/app.gabbro.gabbro/ (vaults) and ~/.config/gabbro/ (settings),
separate from the app files. To erase everything, delete those two directories as
well. (Android differs: uninstalling the app does delete its vaults, because app
data lives in private storage — export a .gabbro backup first.)
Gabbro ships a small gabbro-autotype helper. Nothing is bound by default, so
auto-type does nothing until you bind it to a shortcut yourself. Once you do,
pressing that key types the login currently showing in Gabbro into whatever
window has focus — username, Tab, password, Enter. No copy-paste.
You choose the entry; Gabbro cannot. A browser does not tell the window manager which site is on screen, and Gabbro will never ship a browser extension to find out docs/decisions/ADR-008-no-browser-extension.md. So it does no site matching and cannot warn you when the entry is wrong for the page — whatever login is showing is what gets typed, and submitted. It stays showing until you pick another or the vault locks. Check the entry before you press your key.
Its path depends on how you installed:
- Package (AUR /
.deb):/usr/lib/gabbro/gabbro-autotype - Tarball:
/bundle/gabbro-autotype
qtile (~/.config/qtile/config.py):
Key([mod, "control"], "g", lazy.spawn("/usr/lib/gabbro/gabbro-autotype")),Cinnamon / Linux Mint: Menu → Keyboard → Shortcuts → Custom Shortcuts → Add custom shortcut, with the full path as the command.
Requires an X11 session (not Wayland) and Gabbro running and unlocked with a
login showing. Full instructions, other desktops, and troubleshooting:
docs/AUTOTYPE_AND_AUTOFILL.md.
The Linux tarball is signed with the project's OpenPGP (GPG) key — the same way
the Arch ISO is. Each release ships a detached signature file
(gabbro-) alongside the tarball.
The signing key's fingerprint is:
369B E2CE CFD0 A528 7155 895A 4775 4EEE 7F9A ABFC
Import the public key, confirm the fingerprint matches, then verify the download:
# 1. Import the public signing key
gpg --import <<'KEY'
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEajgxtBYJKwYBBAHaRw8BAQdAG3DemW7XMQSbcWx5koOsGKaBrkF4HMTq+73w
e3t2Fli0IUdhYmJybyBSZWxlYXNlcyA8Z2FiYnJvQHR1dGEuY29tPoiWBBMWCgA+
FiEENpvizs/QpShxVYlaR3VO7n+aq/wFAmo4MbQCGwMFCQPCZwAFCwkIBwIGFQoJ
CAsCBBYCAwECHgECF4AACgkQR3VO7n+aq/z/8QEA3uN7NLlAOOuBr/K7ReMALsxn
QWIUZ395/gfdwJJCsNcA/17lsivsdnCGt8uQogCXF/DUwCwupmT4Fe5+fCOrHbgH
uDgEajgxtBIKKwYBBAGXVQEFAQEHQDvu7ROg/IQiWFO7EH/kUvFQi0/RipJPdEJ9
xgH07nR1AwEIB4h+BBgWCgAmFiEENpvizs/QpShxVYlaR3VO7n+aq/wFAmo4MbQC
GwwFCQPCZwAACgkQR3VO7n+aq/wc8AEA8n4s8olL3l5l28uAHhpwxTUyo7D3TzIq
emmgXWd/v3wA/32c5BlwHzo6dt803Q0tK2neIwrFqKr5dBCZM2nDDK4E
=lC0o
-----END PGP PUBLIC KEY BLOCK-----
KEY
# 2. Confirm the fingerprint printed matches the one above
gpg --fingerprint gabbro@tuta.com
# 3. Verify the tarball against its signature
gpg --verify gabbro-<version>-linux-x86_64.tar.gz.asc gabbro-<version>-linux-x86_64.tar.gzA Good signature line means the build is authentic. GPG may add a warning that
the key is "not certified with a trusted signature" — that is expected and not a
failure; it only means you have not personally signed the key. The fingerprint
match above is your trust anchor.
A bad or missing signature means the file is not an official Gabbro build — do not run it.
The Debian .deb is signed with the same key — verify it the same way:
gpg --verify gabbro_<version>_amd64.deb.asc gabbro_<version>_amd64.debThe AUR package needs no separate check: its PKGBUILD pins the release tarball's
SHA-256, so installing it verifies against the same signed tarball above.
Native file dialogs go through the XDG desktop portal over the DBus session bus. If the portal cannot be reached, Gabbro does not crash — dialogs degrade to a message inviting you to type the path instead.
-
Bare window managers (e.g. qtile) install the portal but never start it. Start it from session init, e.g. in
~/.xinitrc:/usr/lib/xdg-desktop-portal &. -
Hand-rolled
bwrapsandboxes must forward the Wayland socket (or no window appears at all) and the session bus (or the portal is unreachable):bwrap … \ --ro-bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" \ --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \ --ro-bind "$XDG_RUNTIME_DIR/bus" "$XDG_RUNTIME_DIR/bus" \ --setenv DBUS_SESSION_BUS_ADDRESS "$DBUS_SESSION_BUS_ADDRESS" \ …xdg-desktop-portalplus a backend such asxdg-desktop-portal-gtkmust be running in the session.
- Enable Install from unknown sources on your device:
- Android 8+: Settings → Apps → Special app access → Install unknown apps → select your file manager → Allow
- Transfer the APK for your device to it (USB, email, or file transfer). Pick by device type:
gabbro-— modern phones (almost everyone; use this if unsure)-android-arm64-v8a.apk gabbro-— old 32-bit phones-android-armeabi-v7a.apk gabbro-— emulators / Chromebooks-android-x86_64.apk
- Tap the APK file in your file manager to install.
Tested on Android 11+ (including GrapheneOS). YubiKey authentication requires a YubiKey 5 series key (USB-A/C for all devices; NFC where supported).
Manual APK installs do not auto-update. Obtainium installs and updates Gabbro straight from its GitHub Releases, keeping the project's own signature. It is popular with GrapheneOS users.
- Install Obtainium.
- Tap Add App and paste the repo URL:
https://github.com/gabbro-foss/gabbro - Turn on Include prereleases (current builds are alpha, marked pre-release on GitHub).
- Set the APK filter to your device's ABI so the right file is picked:
arm64-v8a(modern phones),armeabi-v7a(old 32-bit), orx86_64(emulators / Chromebooks). - Install. Obtainium then flags each new release for a one-tap update.
Verify the signing fingerprint on first install (below); Obtainium pins it thereafter.
Before installing, confirm the APK was signed by the project's key — this proves it has not been tampered with or repackaged. The signing certificate's public SHA-256 fingerprint is (package name, then fingerprint — copy both lines as they are, AppVerifier pastes them directly):
app.gabbro.gabbro
0F:0A:B8:1B:9B:B8:F0:21:68:25:83:73:17:C6:49:F3:64:F4:47:B0:D0:93:5B:FA:1B:67:82:A9:FF:3A:1D:2C
- GrapheneOS / Accrescent users: install AppVerifier,
open it, pick Gabbro (or the APK file), and check the reported hash matches the one above.
To compare by paste instead, copy the two lines above verbatim — AppVerifier rejects them
if a
Package:orSHA-256:label is included. - Any platform: run
apksigner verify --print-certs gabbro-and compare the-android- .apk SHA-256certificate digest (is whichever file you downloaded —arm64-v8a,armeabi-v7aorx86_64). All three per-ABI APKs are signed by the same key and share this fingerprint.
A mismatch means the file is not an official Gabbro build — do not install it.
Website passkeys live in your vault as ordinary entries, so they sync and back up with it. Hardware-bound passkeys on a YubiKey remain the stronger option — the trade-off is documented in ADR-009.
- Linux: nothing to enable. While Gabbro runs, browsers see it as a
security key: in the browser's passkey prompt pick the security-key option,
then approve in the dialog Gabbro shows. The vault must be unlocked. Tarball
installs need the one-time
/dev/uhidsetup under Installation (the AUR and APT packages do it for you). - Android: Settings → Passwords, passkeys & accounts → add Gabbro as a passkey service (wording varies by Android version; search Settings for "passkeys"). Passkey prompts then offer Gabbro.
Passkeys for native Android apps are off by default: turn on App passkeys in Gabbro's settings. Each app login then makes one network fetch — see Verify no telemetry above.
Desktop-only; also listed in-app under the vault menu → Keyboard shortcuts.
| Shortcut | Action |
|---|---|
Ctrl+L |
Lock the vault |
Ctrl+N |
New entry |
Ctrl+M |
Open the menu |
Ctrl+Q |
Lock and quit (asks first) |
Ctrl+F |
Focus search |
Ctrl+Shift+F |
Search all fields |
Tab / Shift+Tab |
Move between regions (search, folders, filters, list, detail) |
↑ ↓ ← → |
Move within the focused region |
Enter / Space |
Activate the focused control |
Esc |
Leave the focused region; again to close a dialog or go back |
There is deliberately no copy shortcut — copying a secret stays an explicit, auto-clearing action.
NumLock LED switches off when you plug in a YubiKey (Linux/X11). On an X11 session, inserting a YubiKey turns the keyboard's NumLock indicator light off — you may notice this when unlocking a passphrase + YubiKey vault. Your numeric keypad keeps working normally (the digits still type); only the LED is affected.
This is not a Gabbro behaviour and Gabbro cannot prevent it: a YubiKey
presents a USB keyboard interface (the one that types one-time passwords when you
touch it), and X11 resets the keyboard's indicator lights whenever any keyboard
device is plugged in. The same happens with many USB keyboards. It is cosmetic —
nothing to fix and nothing lost. If the wrong LED bothers you, your desktop's
"turn NumLock on at login/after hotplug" option (e.g. numlockx) re-syncs it.
To run the app:
- Flutter
- Rust (
rustup toolchain install stable) - flutter_rust_bridge_codegen
libfido2(Linux YubiKey support)
On Arch Linux, install Flutter via the AUR (flutter-bin) and Rust
via pacman (pacman -S rustup). Add yourself to the flutter group:
sudo usermod -aG flutter $USER
# log out and back inTo run the full test gate (./gabbro_test), also:
cargo install cargo-audit cargo-deny # advisory + licence legs
rustup target add aarch64-linux-android # Android cfg-leak legPlus the Android toolchain, from Android Studio:
- Android Studio in
/opt/android-studio—android/gradle.propertiespointsorg.gradle.java.homethere. Its bundled JBR is a full JDK; no separate one needed. - SDK at the default location (
$HOME/Android/Sdk) — the gate globs that path. - NDK 28.2.13676358, matching Flutter's pin (
ndkVersion = flutter.ndkVersion). Pick it under SDK Tools -> NDK (Side by side) -> Show Package Details. export JAVA_HOME=/opt/android-studio/jbrin your shell profile. Thegradlewlauncher needs a JVM to start;org.gradle.java.homeonly picks the daemon's.
Then, once, before the first gate run:
flutter build apk # creates android/gradlew (gitignored, injected by Flutter)
./gabbro_test --warm # warms the crate, advisory and Gradle caches (online)The gate calls ./gradlew directly rather than through Flutter, so nothing
recreates the wrapper for it — hence the one build first. --warm is also
needed after any dependency change; the offline cargo audit leg is only as
fresh as the last one.
Signing is not part of the gate but is read at configure time, so
android/key.properties and its keystore must exist for any Gradle task,
unit tests included. Both are gitignored: generate your own
(Flutter signing guide).
from gabbro root folder:
flutter pub get
flutter run -d linux # Linux desktop
flutter run -d android # Android device/emulatorfrom gabbro root folder:
flutter build linux --release # Linux desktop
./build/linux/x64/release/bundle/gabbro # Run on linux
flutter build apk --split-per-abi --release # Android (per-ABI APKs)
adb install build/app/outputs/flutter-apk/app-arm64-v8a-release.apk # install on a modern phoneOne script runs every suite — Flutter, real-FFI, Rust, Android — and reports each pass or fail without stopping early. From the repo root:
./gabbro_test # full gate
./gabbro_test --warm # warm caches first (online); needed after a dependency changeThe Rust and Android legs run in a no-network namespace, proving they need no network. See Prerequisites above for what the gate requires.
Using Gabbro
docs/AUTOTYPE_AND_AUTOFILL.md— setting up auto-type on Linux and autofill on Androiddocs/VAULT_SYNC.md— syncing one vault across devices; worked example with Syncthing (Linux) and Syncthing-Fork (Android)docs/VAULT_UPGRADE_PATH.md— vault file format versions and what happens to an older vaultdocs/EMERGENCY_SHEET.pdf— print, fill in by hand, keep on paper: everything needed to open the vault again if the device is lost
Project
docs/ARCHITECTURE.md— full architecture referencedocs/SECURITY.md— security overview: what Gabbro protects against and what it does notdocs/BUILD_AND_RELEASE.md— build environment, runtime dependencies, and the release processdocs/MAINTENANCE.md— vendored data and pins that need periodic refreshdocs/decisions/— architectural decision records (ADRs)
AI development and review
docs/AI_DEVELOPMENT_PROCESS.md— is Gabbro "vibe-coded"? notes on how it is actually builtdocs/AI_AUTHORSHIP_AND_IP.md— who owns AI-written code: authorship and IP notesdocs/AI_SECURITY_AUDIT.md— AI-assisted security review of the crypto and vault modules (Claude Opus 4.7, 2026-05-31)docs/AI_SECURITY_AUDIT_REVIEW.md— second-pass review of that audit (Claude Fable 5, 2026-06-11)docs/AI_SECURITY_AUDIT_3.md— third pass: import parsers, FFI/JNI bridge, Kotlin, Dart leak channels (Claude Opus 4.8, 2026-06-25)
GPL-3.0-only — see LICENSE for details.
This project is in early development. Contributions, feedback, and security review are welcome.
Before contributing, please open an issue to discuss what you have in mind. This applies to bug reports, feature requests, and proposed changes alike.
Gabbro is a security-critical project. All contributions must be human-authored and human-reviewed.
- Agentic pull requests are not accepted. PRs authored or generated by AI agents will be closed without review. This is not a reflection on AI tools generally — it is a recognition that security-sensitive code requires human understanding, human accountability, and human judgement at every step. (See: the curl project's experience with AI contributions for context on why this matters.)
- Agents are welcome to open issues. If an AI assistant has identified a bug, a security concern, or a reasonable feature request, a respectfully written issue is a genuine contribution. Please state clearly that the issue was AI-assisted.
Human reviewers are scarce; their attention is valuable. Please respect that.


