Omahub
← All plugins
D

Bitwarden

by David Spencer

Bitwarden password manager integration for the Omarchy status bar and quick access panel.

Security review

Potentially dangerous behavior detected · 19 findings

Deterministic scan — not a security guarantee

High
Risk level
High
Analyzed commit
6832e10
Scanned
4 days ago

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

Low
AI risk level
Low
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
6832e10
Reviewed
4 days ago

The deterministic scan's high-risk findings are all false positives from test files and documentation: the 'curl | sh' and 'rm -rf /' strings appear only in tests that verify the plugin rejects malicious input, and the apt-get/sudo references are in CI workflows and UI comments, not executed on the user's machine. The sampled production code (QML/JS) is well-structured, handles secrets carefully (keyring, env vars instead of argv), and shows no signs of obfuscation or hidden malicious behavior.

  • All deterministic high/medium findings are in tests, CI workflows, or documentation; none are in executable runtime paths.
  • The plugin handles sensitive credentials and integrates with the OS keyring and FIDO2, so users should review the documented security model, but the code appears deliberately secure.
  • A few low-severity findings (hex escapes) are in test fixtures, not production code.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/Elevate08/qs-bitwarden-cli --enable
Productivity #security

qs-bitwarden-cli

Your Bitwarden vault in the Omarchy status bar. Search, copy, and manage every item type without opening a browser.

License: MIT Version Platform: Omarchy Requires: Bitwarden CLI + jq

Bitwarden Vault Plugin preview

Built on Quickshell and the official Bitwarden CLI. Keyboard-first, fast, and it never writes your vault to a cache of its own.


Install

One command. It clones the plugin, enables it, and puts it in the bar:

omarchy plugin add https://github.com/Elevate08/qs-bitwarden-cli --enable

Nothing else has to be installed first. Open the panel and it tells you what it still needs -- on a stock Omarchy install that is the Bitwarden CLI and jq -- with an Install button that hands off to Omarchy's own installer.

To update: omarchy plugin update io.github.elevate08.qs-bitwarden-cli

Sign in

<img src="docs/screenshots/12-login.png" width="420" align="right" alt="Login screen">

Email and password, with the 2FA prompt appearing only when Bitwarden asks for one. Server region picks US, EU, or a custom URL for self-hosted Bitwarden and Vaultwarden.

Using SSO, a Duo push, or a hardware key? Launch Terminal runs bw login in a real terminal so Bitwarden's own prompts handle it, then hands the session straight back to the panel -- no second login just to get in.

<br clear="all">

The tour

Your vault, one keystroke away

<img src="docs/screenshots/01-vault-list.png" width="420" align="right" alt="Vault list">

Search by name, username, URL, public key or fingerprint. <kbd>Enter</kbd> copies the password and arms the TOTP follow-up; press it again within the window and the live 2FA code replaces it on the clipboard.

Items show their folder and organization inline. The bottom bar filters by folder, organization and type without leaving the keyboard.

Suggestions read the focused window or browser tab and pin the matching credential to the top, so <kbd>Enter</kbd> is usually the only key you need. Picking an item remembers it for the site's domain or the app; for a site whose title names neither, pin it with Suggest here (only a pin learns a title's words, so a look-alike title cannot borrow a real site's login).

Suggestions come from the window title, because that is all Hyprland exposes -- not the tab's real address. A page chooses its own title, so a phishing page titled github.com is suggested your GitHub login just like the real one. Check the address bar before you paste; unlike browser autofill, this is no defence against a look-alike site.

<br clear="all"> <img src="docs/screenshots/06-folder-drawer.png" width="420" align="right" alt="Folder filter drawer">

<kbd>f</kbd>, <kbd>o</kbd> and <kbd>t</kbd> open the folder, organization and type drawers. Arrows move, <kbd>Enter</kbd> applies, <kbd>Esc</kbd> closes -- and the cursor starts on the option already in effect, so <kbd>Enter</kbd> never changes anything by accident.

<br clear="all">

Open an item

<img src="docs/screenshots/02-login-detail.png" width="420" align="right" alt="Login detail">

Username, password and the live TOTP with its countdown, the websites attached to the item, its notes and any custom fields. Copy any of them with one key. An item marked Master password re-prompt asks for your master password before anything secret of it is shown, copied or edited.

Suggest here pins this item for the app or site in front of you, so it is offered outright next time rather than inferred.

<br clear="all">

Every item type, not just logins

<img src="docs/screenshots/04-card-detail.png" width="420" align="right" alt="Card detail">

Cards show cardholder, brand, number, expiry and security code. The number and the code are masked until revealed, and each reveals independently -- an eye is a statement about the field it sits on.

Search finds a card by brand, cardholder, or last four digits. Deliberately not by the middle of a number.

<br clear="all"> <img src="docs/screenshots/05-identity-detail.png" width="420" align="right" alt="Identity detail">

Identities show name, username, company, email and phone, the social security, passport and licence numbers, and the address as a single copyable block rather than seven rows. Empty fields are not drawn, so a sparse identity stays short.

The three identifiers are masked for the reason a password is, with the difference that these cannot be rotated afterwards.

<br clear="all">

Add and edit, without waiting

<img src="docs/screenshots/03-edit-item.png" width="420" align="right" alt="Edit item form">

Create logins, secure notes, cards and identities. <kbd>Enter</kbd> saves from anywhere in the form, so a long item does not have to be scrolled to the bottom.

Saving and deleting no longer hold the panel. The form closes as the command is launched and the row shows a spinner until the vault answers. If the vault refuses, the list goes straight back to what it actually holds and offers to reopen what you typed.

<br clear="all">

Generator

<img src="docs/screenshots/08-generator.png" width="420" align="right" alt="Password generator">

Every option the browser extension has -- length, character classes, minimums, ambiguous characters, or a passphrase with a word count and separator -- with a live strength meter.

Generation comes from Bitwarden's own generator, not a reimplementation, and answers in about 2ms rather than the ~2.9s a fresh bw generate costs.

<br clear="all">

Send

<img src="docs/screenshots/09-sends.png" width="420" align="right" alt="Bitwarden Send">

Share a secret through a link that expires on its own, so a credential need not live in a chat log. Set a deletion window, a view limit and an optional password; the link is copied the moment it is created.

<br clear="all">

SSH agent

<img src="docs/screenshots/13-ssh-approval.png" width="420" align="right" alt="SSH signing approval">

Opt-in. Serves the SSH keys in your vault to ssh, Git and ssh-keygen -Y sign while the vault is unlocked, from a helper process that holds the private keys in memory -- never on disk, never in QML.

Every signature says what is being signed and names the key, its fingerprint and the program asking. One approval can cover a whole rebase -- and only the rebase's signatures; live grants are listed and revocable.

Setup, verification and threat model →

<br clear="all">

Settings

<img src="docs/screenshots/10-settings.png" width="420" align="right" alt="Settings screen">

Grouped into General, Security and SSH Agent, with the section you are reading pinned above the list as you scroll. Destructive actions sit under their own DANGER ZONE heading.

Changes are written to the plugin's entry in ~/.config/omarchy/shell.json through omarchy bar set, so Omarchy owns the file and the shell hot-reloads.

The General settings include Colorize menu-bar icon, which makes the primary Bitwarden shield follow the active Omarchy theme accent. It is off by default; lock and setup/error indicators keep their existing status colors.

<br clear="all">

How it compares

What this plugin does, next to the two official Bitwarden clients a Linux user would otherwise reach for. Checked against Bitwarden's documentation on 2026-12-01.

This plugin Bitwarden CLI Bitwarden Desktop
Lives in the Omarchy bar ✅ ❌ ❌
View logins, notes, cards, identities ✅ ✅ ✅
Create / edit logins, notes, cards, identities [^cli-json] ✅ ✅ ✅
View SSH key items ✅ ✅ ✅
Create / import SSH keys [^adr] [^ssh-clients] ❌ ❌ ✅
SSH agent [^ssh-desktop] ✅ ❌ ✅
TOTP codes, auto-copied after the password [^totp] ✅ ❌ ❌
Download attachments ✅ ✅ ✅
Bitwarden Send, text ✅ ✅ ✅
Folders, collections, organizations ✅ ✅ ✅
Password / passphrase generator ✅ ✅ ✅
Unlock with PIN [^pin] ✅ ❌ ✅
Unlock with fingerprint [^fp] [^bio] [^bio-linux] ✅ ❌ ✅
Unlock with FIDO2 key [^fido] ✅ ❌ ❌
Auto-lock on idle, screen lock, suspend [^cli-lock] [^desk-lock] ✅ ❌ ✅
Suggests by focused window / browser tab ✅ ❌ ❌
Self-hosted and Vaultwarden ✅ ✅ ✅
Import / export your vault [^io] ❌ ✅ ✅
Trash: restore a deleted item [^trash] ❌ ✅ ✅
Upload attachments [^attach] ❌ ✅ ✅
File Sends [^filesend] ❌ ✅ ✅
View / edit custom fields [^fields] ✅ ✅ ✅
Organization admin: confirm members, approve devices [^orgadmin] ❌ ✅ ❌

[^cli-json]: The CLI creates a login by default; other types need the JSON edited before encoding, as its documentation describes -- "use a command-line JSON processor like jq to change a .type= attribute to create other item types." [^adr]: This plugin will not. The CLI can encrypt a type-5 item, but generating a key means putting private material somewhere this plugin has deliberately kept it out of. [^ssh-clients]: Bitwarden documents SSH keys as generated or imported "using the desktop app, web app, and browser extension", and generation is Ed25519 only. [^ssh-desktop]: Bitwarden's SSH agent is a desktop-app feature; the CLI does not provide one. [^pin]: PIN unlock is documented for "mobile apps, browser extensions, and desktop apps". [^fp]: This plugin verifies through the same PAM stack as the Omarchy lock screen, so it works wherever omarchy setup security fingerprint has been run. [^bio]: Biometric unlock is documented for the desktop app, browser extensions and mobile apps -- not the CLI. [^bio-linux]: On Linux the desktop app's biometric unlock goes through a polkit agent rather than a fingerprint reader directly. [^fido]: This plugin asks the key for its hmac-secret on the registration omarchy setup security fido2 writes, and that secret opens the stored password -- so unlocking needs the key, not only a touch. No Bitwarden client offers FIDO2 unlock, so there is no upstream column to match. [^totp]: All three read TOTP codes -- bw get totp on the CLI. The check here is for the follow-up: <kbd>Enter</kbd> copies the password and then replaces it with the live code a few seconds later, so a login and its second factor are one keystroke apart. [^cli-lock]: The CLI has bw lock, but no timeout of its own -- a session key stays valid until something locks it. [^desk-lock]: The desktop app offers time passed, on system idle, on system sleep, on system lock and on restart.

[^io]: bw import and bw export on the CLI; the desktop app has both in its UI. This plugin has neither -- it reads and writes single items, and a vault export is a different kind of operation from the one it is for. [^trash]: A delete here is a delete. Bitwarden keeps deleted items in a trash for 30 days and both official clients can restore from it (bw restore); this plugin shows no trash and cannot restore. [^attach]: This plugin downloads attachments but cannot add one. The CLI has bw create attachment --file. [^filesend]: This plugin creates text Sends only. Both official clients send files too -- bw send -f <path>. [^fields]: Text, hidden, boolean and linked fields follow the same type model as Bitwarden's browser extension. Secure Notes have no linked native field, so the linked type is offered only for logins, cards and identities. [^orgadmin]: bw confirm and bw device-approval are CLI features; the desktop app does not do this either, and it is otherwise the web vault's job. Listed because the CLI is genuinely ahead of both here.

Sources: CLI · SSH agent · About SSH · PIN unlock · Biometrics


Usage & Keyboard Shortcuts

The panel opens with the item list focused, so single-letter shortcuts work straight away. Press <kbd>/</kbd> to type a search.

While the search box has focus every letter is search text -- as a text field should behave. Hold <kbd>Alt</kbd> to reach the same shortcuts without leaving the box or disturbing your query; <kbd>↓</kbd> also hands focus back to the list.

Vault List View (Main Screen)

Shortcut Action
<kbd>Enter</kbd> Copy password (and arm the TOTP follow-up), or open the item when there is no password to copy -- a card, an identity, a note, an SSH key
<kbd>Enter</kbd> (again) Copy the TOTP code during the follow-up window
<kbd>↑</kbd> / <kbd>↓</kbd> / <kbd>j</kbd> / <kbd>k</kbd> Move through items, or through an open filter drawer
<kbd>/</kbd> Focus the search box
<kbd>Tab</kbd> / <kbd>Shift+Tab</kbd> Cycle types without opening the drawer
<kbd>p</kbd> (or <kbd>y</kbd>) Copy password
<kbd>u</kbd> (or <kbd>c</kbd>) Copy username / email
<kbd>m</kbd> Copy TOTP multi-factor code
<kbd>w</kbd> Open the website in your browser
<kbd>e</kbd> Open the detail inspector / edit
<kbd>f</kbd> Folders filter
<kbd>o</kbd> Organizations filter
<kbd>t</kbd> Types filter
<kbd>g</kbd> Generator
<kbd>n</kbd> New vault item
<kbd>s</kbd> Settings
<kbd>r</kbd> Sync (refresh)
<kbd>l</kbd> Lock the vault
<kbd>Alt</kbd>+<kbd>s</kbd> Bitwarden Send
<kbd>Alt</kbd>+<kbd>,</kbd> Settings
<kbd>Alt</kbd>+<kbd>a</kbd> Accounts: switch to another account, or add one
<kbd>Esc</kbd> Close the filter drawer, clear the search, or close the panel

Alt + any letter above runs the same action from inside the search box. Three are Alt-only: <kbd>Alt</kbd>+<kbd>s</kbd> opens Send (which has no bare letter, since <kbd>s</kbd> is Settings), <kbd>Alt</kbd>+<kbd>,</kbd> opens Settings, so Settings is still reachable while searching, and <kbd>Alt</kbd>+<kbd>a</kbd> opens Accounts.

Detail Inspector

Shortcut Action
<kbd>Enter</kbd> / <kbd>y</kbd> / <kbd>p</kbd> Copy what the item is for: the password on a login, the number on a card
<kbd>u</kbd> / <kbd>c</kbd> Copy username; on an identity <kbd>u</kbd> is the username and <kbd>c</kbd> the email
<kbd>n</kbd> Copy a card's number
<kbd>k</kbd> Copy a card's security code
<kbd>m</kbd> Copy TOTP code
<kbd>v</kbd> Toggle reveal on the item's principal secret -- the password on a login, the number on a card. Every other masked field has its own eye, and each reveals independently
<kbd>a</kbd> Save every attachment on this item
<kbd>e</kbd> Edit this item
<kbd>x</kbd> Delete this item (asks first)
<kbd>b</kbd> / <kbd>q</kbd> / <kbd>Esc</kbd> Back to the list

Filter Drawer (Folders / Organizations / Types)

Shortcut Action
<kbd>f</kbd> / <kbd>o</kbd> / <kbd>t</kbd> Open (or close) that drawer
<kbd>↑</kbd> / <kbd>↓</kbd> Move through the options
<kbd>Enter</kbd> Apply the highlighted option
<kbd>Esc</kbd> Close without changing anything

The cursor starts on the option already in effect, so <kbd>Enter</kbd> never changes a filter by accident.

Settings Screen

Shortcut Action
<kbd>↑</kbd> / <kbd>↓</kbd> Move between settings
<kbd>←</kbd> / <kbd>→</kbd> Decrease / increase a number by its step, or switch a toggle off / on
<kbd>Enter</kbd> Flip the highlighted toggle, or open the PIN / fingerprint form
<kbd>Esc</kbd> Back

Send Screen

Shortcut Action
<kbd>Alt</kbd>+<kbd>s</kbd> Open Sends
<kbd>n</kbd> New Send
<kbd>r</kbd> Refresh the list
<kbd>x</kbd> Delete the highlighted Send
<kbd>Enter</kbd> Copy the highlighted Send's link
<kbd>Esc</kbd> Back


Optional features

Several accounts

<img src="docs/screenshots/14-accounts.png" width="420" align="right" alt="Account list">

The panel can hold up to ten Bitwarden accounts at once -- a personal and a work account, say, or two servers. Press Add Account on the locked screen (or the account button in the header) and sign in; the first account stays signed in beside it. Switch Account on the locked screen, or the header's account button, lists them.

  • Each account keeps its own sign-in and its own quick unlock. A PIN, fingerprint or FIDO2 key set up for one account is that account's; switching never asks you to log in again or set anything up again. The same fingerprint or key can unlock every account -- nothing is re-enrolled, each account's stored password just gets its own way in.
  • Only one account is unlocked at a time. Switching locks the one you leave, drops its items from memory and tells the SSH agent, then shows the lock screen of the one you picked, with its unlock methods ready.
  • Log Out signs out of the account on screen only, deletes its stored password and learned suggestions, and moves to the next account. The others are untouched.
  • The quick-unlock settings are shared switches: turning PIN, fingerprint or FIDO2 unlock off in settings removes it from every account.

The first account lives where a terminal bw finds it (~/.config/Bitwarden CLI), exactly as before. Each account added beside it gets a private directory of its own under ~/.local/share/qs-bitwarden-cli/accounts/, which bw is pointed at with BITWARDENCLI_APPDATA_DIR; the list of accounts (emails and servers, nothing secret) is registry.json in the same place. Upgrading needs nothing: your current account becomes the first one, with everything it had.

How your vault is held

While your vault is open, its session key and every item's secrets are held by a small helper process (bin/x86_64-linux/qs-bitwarden-vault), not by the shell. If the shell crashes, the core dump systemd keeps has no session key and no passwords in it, and the shell's own crash dumps still work for diagnosing it. A password you copy goes from the helper straight to the clipboard; TOTP codes and search are answered by the helper too. It talks only to the shell that started it, on its stdin and stdout.

It ships and is verified like the other helpers. If it is missing or fails its check, the panel works as before, with the vault held in the shell, and a banner says crash protection is off. How it works and what it does not cover →

How quick unlock stores your password

PIN, fingerprint and FIDO2 unlock all need your master password, because bw unlock accepts nothing else. The plugin keeps it once per account, in a single keyring item (service=qs-bitwarden-cli, account=unlock_envelope, or unlock_envelope@<slot> for an account added beside the first):

  • The first time bw accepts a password you typed -- a login, or unlocking with your master password -- it is encrypted under a random key with XChaCha20-Poly1305, and the whole item is sealed to this machine and your user with systemd-creds --user. A copy taken off this machine is useless.
  • Each unlock method you turn on adds its own way to that one key, and none of them stores the password again. Turning a method off in settings removes only its way in -- from every account, since the switch is shared -- and one turned off in shell.json while the shell was not running is removed at the next start.
  • The master password you are asked for when turning a method on is a check against the stored one, not a new copy: a wrong one is refused, and nothing you type there is stored.
  • If you change your master password elsewhere, the next unlock with the new one re-seals the stored copy and keeps every method.
  • Logging out of an account deletes its copy; other accounts keep theirs.

The small helper that does the encryption (bin/x86_64-linux/qs-bitwarden-unlock-key) ships and is verified exactly like the SSH helper; if it is missing or fails its check, quick unlock is unavailable and your master password still works. Everything else is the operating system: argon2, systemd-creds, fido2-assert and secret-tool.

An earlier build could store this item across several lines, which makes gnome-keyring refuse Omarchy's passwordless default keyring at the next login (apps ask you to create a new keyring, and the journal says keyring was in an invalid or unrecognized format). The plugin repairs this on its own each time it starts: an item the keyring still serves is stored again on one line, and a keyring file already refused has the item joined back onto one line in place, with the original kept beside it as Default_keyring.keyring.before-repair-<time>. A notification then asks you to restart the computer (Omarchy has no logout), which loads the collection cleanly. To do it by hand, run scripts/repair-keyring.sh from the plugin's directory (--check only reports). Nothing else in the keyring is changed, and no secret is printed.

Upgrading from 1.10.0 or earlier needs nothing from you. The old entries -- a plaintext copy for fingerprint and for FIDO2, an encrypted one for the PIN -- are moved in as each method is next used, and deleted once the new copy opens.

The honest limit. The stored password is only as protected as the weakest method you have turned on, and the settings screen says so. The seal ties the stored item to this machine and this user, so a copy taken elsewhere is useless -- but a program running as you can unseal it here (systemd-creds --user decrypt). With fingerprint unlock on, that is all it needs: a fingerprint releases no secret to encrypt with. With a PIN, it can then guess the PIN offline, at its own pace and on every core (see PIN unlock). A FIDO2 key cannot be bypassed that way, because the key itself produces the secret; root, as always, can read anything.

PIN unlock

Turn on Unlock with PIN in the settings screen. Confirm your master password and choose a PIN of at least 6 digits; 8 or more is the recommendation. Six and seven are accepted but shown with the real cost of guessing them, so a weak PIN is a decision rather than an accident. (A PIN set with an older version, when 4 was the floor, still unlocks; set a new one to get the longer PIN.)

The PIN reaches the stored password through Argon2id (256 MiB of memory, 4 passes), from the OS argon2 tool. That cost is the whole defence against guessing: a program running as you can copy the stored item, unseal it and try PINs offline on every core. Measured on a 16-thread laptop, one guess takes about 0.46 s and 16 in parallel make about 17 a second, so every PIN of 6 digits falls in about 16 hours, 7 digits in about 7 days, and 8 digits in about 2 months (4 digits took about 10 minutes, which is why it is no longer allowed). A wrong PIN always fails -- the encryption is authenticated, so there is no "decrypts to garbage" case.

Five wrong attempts at the panel removes the PIN's way in, and turning it back on needs your master password. That limit applies only to guesses typed into the panel's own screen, not to a copy of the stored item; the Argon2 cost is what stands behind it.

Fingerprint unlock

Set fingerprintUnlock to true to unlock the vault with a finger instead of your master password.

Requirements

  • A fingerprint reader with at least one enrolled finger, configured through omarchy setup security fingerprint. The plugin verifies all of this itself (/etc/pam.d/omarchy-lock-fingerprint, fprintd-list) and silently stays hidden when any part is missing.
  • A running, unlocked OS keyring, as used by rememberSession. Omarchy ships libsecret itself, so there is nothing to install for this.

How it works

  1. Switch Unlock with fingerprint on in the settings screen and confirm your master password.
  2. On every later lock, opening the panel arms the reader. A verified fingerprint opens the stored password for bw unlock; the password field remains available as a fallback at all times.

Security trade-off -- read before enabling

PAM can prove that you are present, but a fingerprint releases no secret, so it cannot encrypt anything by itself. Fingerprint unlock is the one method whose way in is protected only by the machine seal, which means a program running as you while you are logged in can open the stored password without your finger. This is the same trade the official Bitwarden desktop client makes for its own biometric unlock. It is off by default and worth leaving off on a shared or unattended machine -- and with it on, a PIN or a key does not make the stored password any safer.

Its way in is removed when you turn the setting off or log out -- for the account on screen; other accounts keep theirs.

A closed lid takes the option off the screen. The reader is on the laptop body, so with the lid shut -- clamshell mode, or simply closed on a docked machine -- there is nothing to touch. While it is down, the locked screen hides Unlock with Fingerprint, the SSH prompt does the same, and the reader is not armed on open. Omarchy's own detector (omarchy-hw-laptop-closed) decides, and a machine with no lid never reports one. Nothing is forgotten: the settings toggle is unchanged, and the option is back the moment the lid opens. A FIDO2 key on a cable is unaffected.

FIDO2 key unlock

Set fidoUnlock to true to unlock the vault with a FIDO2 authenticator (a YubiKey or any other compliant key) instead of your master password.

Requirements

  • A FIDO2 key registered through omarchy setup security fido2. That one command detects the key, installs libfido2/pam-u2f, registers it, and wires it for the system's own authentication prompts; the plugin uses that same registration (/etc/fido2/fido2) and offers the command itself when no key is registered yet. The option stays hidden on a machine with no key.
  • A key that supports the hmac-secret extension, which current YubiKeys and most FIDO2 keys do.

How it works

  1. Switch Unlock with FIDO2 key on in the settings screen. If no key is registered, it first hands off to omarchy setup security fido2; otherwise confirm your master password and touch the key once.
  2. On every later lock, opening the panel arms the key if one is plugged in (falling back to the fingerprint reader otherwise). One touch asks the key for a secret only it can produce -- its hmac-secret for the registered credential -- and that secret opens the stored password. The password field remains available as a fallback at all times.

The key will not produce that secret without a touch, so unlocking needs the key itself, not just a program able to read your keyring. Each registered key that is plugged in when you turn the option on gets its own way in, so a backup key works too.

Registrations made with +pin or +verification (a key PIN at every system prompt) are not used: the panel cannot collect the key's PIN yet, and a touch alone would be weaker than what the registration asks of the system.

Its way in is removed when you turn the setting off or log out -- for the account on screen; other accounts keep theirs. omarchy remove security fido2 unregisters the key for the system's own authentication prompts as well, which is why the plugin points at Omarchy's setup rather than registering the key itself.

SSH agent

Off by default. See docs/ssh-agent.md for setup, the provenance check, and what the agent does and does not protect.


Bar placement

--enable already puts the widget in the bar. To move it:

omarchy bar move io.github.elevate08.qs-bitwarden-cli --section right

Settings are editable from the panel's own settings screen, or directly in ~/.config/omarchy/shell.json. Each setting lives inline on the bar entry, not in a separate block:

{
  "bar": {
    "layout": {
      "right": [
        {
          "id": "io.github.elevate08.qs-bitwarden-cli",
          "autoLockMinutes": 15,
          "lockOnScreenLock": true,
          "lockOnSuspend": true,
          "clearClipboardSec": 30,
          "rememberSession": true,
          "fingerprintUnlock": false,
          "fidoUnlock": false,
          "sshAgentEnabled": false,
          "sshAgentApprovalPopup": true
        }
      ]
    }
  }
}

Global hotkey

To toggle the Bitwarden panel with a keyboard shortcut (e.g. SUPER + CTRL + /), add the binding to ~/.config/hypr/bindings.lua:

o.bind("SUPER + CTRL + SLASH", "Bitwarden vault", "omarchy-shell io.github.elevate08.qs-bitwarden-cli toggle")

Apply changes by restarting the shell:

omarchy restart shell

Configuration Reference

The following settings are read from the plugin's own entry in the bar.layout array of ~/.config/omarchy/shell.json -- inline alongside its id, as shown above. The panel's settings screen writes them for you via omarchy bar set, so editing the file by hand is optional:

Key Type Default Description
autoLockMinutes number 15 Minutes of inactivity before automatically locking the vault (0 to disable). Range 0-1440; out of range is clamped and an unreadable value falls back to 15.
clearClipboardSec number 30 Seconds before automatically clearing copied secrets from the clipboard (0 to disable). The clear is the copy's own lifetime (wl-copy under timeout), so it survives a shell restart and never touches something copied later. Range 0-300; out of range is clamped and an unreadable value falls back to 30.
lockOnScreenLock boolean true Lock the vault as soon as the screen locks, rather than waiting out autoLockMinutes. Reads the Omarchy lock screen's own state, so it follows a manual lock and an idle lock alike. A shell without the lock plugin simply never reports a lock; it is never read as one.
lockOnSuspend boolean true Lock the vault when the machine is going to sleep, so no unlocked session key is left in the suspended machine's memory. Holds a delay sleep inhibitor until bw lock and the keyring clear have finished, at most 4 seconds. Needs gdbus (glib2), systemd-inhibit and setsid (util-linux). The monitor and its children stop when the plugin unloads; without these tools the setting is inert.
rememberSession boolean true Persist session token in OS keyring (secret-tool) while unlocked. Survives a shell restart, never a reboot -- see the note above. Turning it off removes a stored session at once, and with it off an unload while unlocked runs bw lock.
autoCopyTotpSec number 3 Seconds after password copy to automatically replace clipboard with TOTP code (0 to disable). Range 0-30; out of range is clamped and an unreadable value falls back to 3.
closeOnCopy boolean true Automatically close panel on Enter copy so target application receives focus immediately.
suggestOnOpen boolean true Automatically suggest matching vault items for the active window or browser tab on open.
fingerprintUnlock boolean false Unlock the vault with an enrolled fingerprint. Adds a way into the one encrypted stored password -- see Fingerprint unlock for its limit.
fidoUnlock boolean false Unlock the vault with a FIDO2 authenticator, on the registration omarchy setup security fido2 writes. The key's hmac-secret opens the stored password -- see FIDO2 key unlock.
pinUnlock boolean false Unlock with a numeric PIN of at least 6 digits, through Argon2id -- see PIN unlock.
sshAgentEnabled boolean false Serve your vault's SSH keys to ssh, Git and signing while the vault is unlocked. Starts a helper process and a socket under $XDG_RUNTIME_DIR; private keys stay in that helper and are dropped on lock -- see SSH Agent.
sshAgentUnlockOnDemand boolean false Let an identity listing raise the unlock prompt when the vault is locked and no keys have been loaded yet, instead of answering with an empty list. Signing a key the helper already knows always raises the prompt, with or without this. Off by default: ssh asks the agent for identities on every connection, so this raises the configured approval surface on the first ssh after every login.
sshAgentApprovalPopup boolean true Show SSH unlock and signing requests in a transient card in the middle of the screen instead of opening the anchored panel. Disable to show prompts in the panel. Multiple concurrent requests are queued sequentially with a "1 of N" counter and "Deny all" option. Escape and outside click deny.
sshAgentApprovalWindowSec number 120 How long one approval keeps covering further signatures from the same program with the same key. Range 0-900; 0 asks every time. Held in memory only and dropped on lock, logout or exit.

One further key, twoFactorMethods, is written to the same entry but is not a setting you configure. It records which two-step method last logged each account in, keyed by login address -- {"you@example.com": 0}, where 0 is authenticator, 1 email and 3 YubiKey -- so an account with more than one method is asked only once, and two vaults on one machine each keep their own answer. Change method on the code screen asks again and rewrites it. Entries are capped at ten accounts, and anything unreadable is treated as not remembered, which costs that account one extra prompt.

Learned suggestions are stored separately in ~/.local/state/qs-bitwarden-cli/associations.json. Delete that file to reset everything the panel has learned; logging out deletes it for you.



Multiple monitors

Omarchy draws its bar once per monitor, so the widget appears on every one of them -- but there is one vault behind them all:

  • Unlocking or locking on any monitor applies to all of them, and every bar's icon shows the same state.
  • The panel opens on the monitor whose icon you click, and moving to another monitor's icon moves the open panel there, where you left it. A keyboard summon lands on the focused monitor, or on the panel if it is already open.
  • There is one SSH agent, one sleep inhibitor and one IPC target however many monitors are attached. A signing request appears on the monitor with the open panel, or otherwise the focused one.
  • Unplugging a monitor, even the one showing the panel, leaves the vault and the agent running.

If something else on the machine is already serving the agent's socket -- a second Omarchy shell, for example -- the SSH agent settings say so and wait for it to stop, rather than reporting a helper that keeps failing to start.

A bar other than Omarchy's own cannot share one vault between its copies, so on a replacement bar each monitor's widget keeps a vault of its own, as before.

IPC & Scripting Interface

You can control and query the Bitwarden plugin from the terminal, scripts, or window manager bindings. The form is omarchy-shell <target> <method>:

# Show, hide, or toggle the popup panel
omarchy-shell io.github.elevate08.qs-bitwarden-cli open
omarchy-shell io.github.elevate08.qs-bitwarden-cli close
omarchy-shell io.github.elevate08.qs-bitwarden-cli toggle

# Jump straight to a screen
omarchy-shell io.github.elevate08.qs-bitwarden-cli settings     # -> "settings"
omarchy-shell io.github.elevate08.qs-bitwarden-cli setup        # -> "setup" (dependency wizard)

# Lock the vault immediately
omarchy-shell io.github.elevate08.qs-bitwarden-cli lock         # -> "locked"

# Sync with Bitwarden
omarchy-shell io.github.elevate08.qs-bitwarden-cli sync         # -> "syncing"

# Query vault state
omarchy-shell io.github.elevate08.qs-bitwarden-cli status       # -> "unlocked" | "locked" | "unauthenticated"

# Which vault the bars share, and which monitor presents it (non-secret)
omarchy-shell io.github.elevate08.qs-bitwarden-cli vaultHost    # -> {"host":"shared","views":2,"presenter":"DP-1",...}

# The accounts the panel holds (emails and servers only), and switching to one;
# switching locks the account active now
omarchy-shell io.github.elevate08.qs-bitwarden-cli accounts     # -> {"adding":false,"accounts":[{"email":"you@example.com","server":"","active":true}]}
omarchy-shell io.github.elevate08.qs-bitwarden-cli switchAccount work@example.com   # -> "switching" | "unknown" | "busy"

open, close and toggle return nothing; the rest echo the state they moved to.

Omarchy's shell-level dispatcher also toggles any plugin, and works equally well for a keybinding:

omarchy-shell shell toggle io.github.elevate08.qs-bitwarden-cli

Only toggle exists at that level, though -- omarchy-shell shell open|close <id> answers Function not found, and omarchy-shell shell call <id> <method> '{}' answers unknown. Use the plugin-target form above for everything other than toggling.

The same calls work through Quickshell directly, which is useful when omarchy-shell is not on PATH:

qs -p /usr/share/omarchy/shell/shell.qml ipc call io.github.elevate08.qs-bitwarden-cli status


Dependencies

The helper's crates are updated by Dependabot under versioning-strategy: lockfile-only, so a proposed bump only ever moves agent/Cargo.lock within the bounds agent/Cargo.toml already allows. Crossing a major is a manual edit, on purpose: several of the version floors in that manifest exist to keep one copy of the RustCrypto traits in the graph, and Dependabot raising them one at a time is what broke the build in PR #11.

The crypto stack moves together or not at all. ssh-key and rsa re-export the trait generation their callers must match, and agent/src/lib.rs calls those traits directly -- Verifier::verify, try_sign, pkcs1v15::SigningKey. Bump one crate without the others and cargo resolves two versions side by side, at which point the traits stop unifying and nothing compiles. The coupled set is ssh-key, ssh-encoding, ed25519-dalek, rsa, signature, sha2/digest, zeroize and rand_core; Dependabot groups them under crypto for the same reason.

As of 2026-08-31 that upgrade is gated upstream: ssh-key is at 0.7.0-rc.11 and rsa at 0.10.0-rc.18, both still pre-release, and the stable releases still pin the older generation. When they land, raise every crate in the set in one commit and expect real source changes, not just a manifest edit. Nothing will prompt you -- there are no ignore conditions to trip, because cargo's own semver rules already hold 0.10 back from 0.11.

Every accepted bump, major or not, changes the shipped bytes and so needs the binary rebuilt in the same change, by hand -- see the needs-binary-rebuild label:

gh pr checkout <n>
./scripts/build-agent.sh    # re-enters the digest-pinned image
git commit -am "deps: rebuild the agent binary" && git push

For other source changes CI does it: on a pull request into a release branch from this repository, helper-rebuild.yml rebuilds the helpers in the pinned image and commits them to the PR branch. It never does so when Cargo.lock, Cargo.toml or rust-toolchain.toml changed, or for Dependabot or a fork. --compare-tracked proves the committed bytes are what the committed source builds; it cannot tell you whether that source is trustworthy, and a malicious crate builds just as reproducibly as an honest one. Reading the Cargo.lock diff before you commit the binary is the only check that covers that, which is why dependency rebuilds stay a human step.



More


License

MIT -- see LICENSE.