# Features in detail Every feature the plugin has, and why each one works the way it does. The [README](../README.md) is the tour; this is the reference behind it. - **Full Status Bar & Quick Access Panel Integration**: - Live vault lock state indicated in the status bar (`󰞀` shield icon: accent when unlocked, base when locked, urgent when unauthenticated). - Clean drop-down panel with fast navigation and keyboard-first workflow. - **Authentication & Secure Keyring Storage**: - Direct Master Password unlock. - Interactive login starts with Email + Password and reveals the 2FA prompt only when Bitwarden requires it (Authenticator App or Email); API Key credentials (`BW_CLIENTID` / `BW_CLIENTSECRET`) remain available as a separate method. - **Custom Server Support**: Works seamlessly with official Bitwarden servers and self-hosted Vaultwarden instances. - **Terminal login fallback**: when the built-in form cannot cover your login method -- SSO, a Duo push, a hardware key -- the login screen offers **Launch Terminal**, which runs `bw login` in a real terminal so Bitwarden's own prompts handle it. - That terminal hands its session straight back: it captures the key with `bw login --raw` (prompts stay on stderr, so the login is still interactive) and writes it to `$XDG_RUNTIME_DIR/qs-bitwarden-cli/session-handoff`, mode `600`, in a directory created `700` before the file exists. There is no fallback path: if `XDG_RUNTIME_DIR` is somehow unset the login refuses to run rather than putting a session key anywhere a second user could have prepared. The panel reads that file once, deletes it, and comes back unlocked -- no second login just to get in. If the vault was merely locked rather than logged out, the same button unlocks instead. **It only reads it when it is expecting to.** The check runs on every status refresh, and it used to adopt whatever was at that path whether or not the panel had ever asked for a terminal login -- so anything able to write the file could hand the panel a session key at a moment of its own choosing, and the panel would take it and write it to the keyring. The runtime directory is `0700`, so that is one of your own processes rather than a stranger and it was never a privilege boundary; it was a window with no reason to be open. A key is only expected in the ten minutes after the panel itself launched a terminal, so those are the only minutes it is read in. Outside them the file is deleted unread -- not reading it is not a reason to leave a live session key lying in the runtime directory, and a login abandoned halfway leaves exactly that. - **The terminal reopens the panel for you** on success, then closes itself; you only have to dismiss it if something went wrong and there is an error worth reading. - Two `bw status` calls used to sit on that path, each around three seconds on a real vault: one in the terminal to decide login-versus-unlock, one in the panel to confirm a key `bw` had just minted. Neither is needed -- the panel already knows which state it is in, and the confirming check now starts only after the item list has been rendered instead of sitting in front of it. - **Several accounts at once**: - Up to ten accounts, each signed in on its own: **Add Account** signs another one in without signing the first out, and **Switch Account** moves between them. Only one is unlocked at a time -- switching locks the one being left, drops its items and secrets from memory, and tells the SSH agent the account changed so its keys and exported public keys go too. - `bw` keeps one account per data directory, so each account added beside the first gets its own, `~/.local/share/qs-bitwarden-cli/accounts//` (mode `700`), given to every `bw` the panel runs in `BITWARDENCLI_APPDATA_DIR`. The first account stays in `bw`'s own directory, so a terminal `bw` still sees it and upgrading changes nothing for it. - Each account's keyring entries -- session, stored password, and the legacy per-method entries -- are named with the account's slot (`unlock_envelope@`), and `secret-tool` matches names exactly, so no lookup for one account can find another's. The first account keeps the names it always had. - PIN, fingerprint and FIDO2 unlock are per account: each account's stored password has its own ways in, so the same finger or key unlocks every account with nothing re-enrolled. - **Log Out** clears only the account on screen: its keyring entries, its learned suggestions and its data directory. Signing the same account in twice keeps the newer sign-in and removes the older one. - The list (`registry.json` beside the directories, mode `600`) holds only emails, user ids and servers, and anything malformed in it is dropped rather than trusted. - **One stored password for quick unlock**: - The master password is kept once, in one keyring item: encrypted under a random key (XChaCha20-Poly1305) and sealed to this machine and user with `systemd-creds --user`. It is written the first time `bw` accepts a password you typed, and each quick-unlock method only adds a way into it. See [How quick unlock stores your password](../README.md#how-quick-unlock-stores-your-password). - Turning a method on asks for your master password as a check against the stored one -- a wrong one is refused, and nothing typed there is stored. - A master password changed elsewhere is picked up at the next unlock with the new one; every method keeps working. - Turning a method off removes it from every account (the setting is shared), and a method turned off in `shell.json` while the shell was not running is removed at the next start. - Upgrading moves the old per-method entries in as each method is next used, and deletes them once the new copy opens. - **PIN Unlock** (opt-in, `pinUnlock`): - Unlock with a numeric PIN instead of typing the master password. **6 digits is the floor and 8 or more the recommendation**, and there is no upper limit. A 6- or 7-digit PIN is accepted, with the real cost of guessing it spelled out. A PIN set before the floor was raised (from 4) still unlocks. - The PIN reaches the stored password through Argon2id (256 MiB, 4 passes). The stored item is sealed to this machine, but a program running as you can unseal it and guess PINs offline on every core: about 17 guesses a second on a 16-thread laptop, so every 6-digit PIN in about 16 hours, 7 digits in about 7 days, 8 digits in about 2 months. - A wrong PIN always fails; there is no stored PIN hash. - Five wrong attempts at the panel's own screen removes the PIN's way in; re-enabling needs the master password again. The limit does not apply to a copy of the stored item. - **Fingerprint Unlock** (opt-in, `fingerprintUnlock`): - Unlock the vault with an enrolled fingerprint instead of retyping your master password. - Verifies through the same PAM stack as the Omarchy lock screen (`/etc/pam.d/omarchy-lock-fingerprint`), so it works wherever `omarchy setup security fingerprint` has been run. - Turning it on asks you to confirm your master password up front, rather than quietly capturing it on some later unlock. - The reader is armed automatically whenever you open the panel on a locked vault; the master password field always stays available as a fallback. - A closed lid takes the option off the screen: the reader is on the laptop body, so with the lid shut (clamshell mode, or a lid closed on a docked machine) the unlock button is hidden and the reader is not armed, on the locked screen and in the SSH prompt alike. Nothing is forgotten, and the option returns when the lid opens. Omarchy's `omarchy-hw-laptop-closed` decides. - A fingerprint releases no secret, so with it on a program running as you can open the stored password. See [Fingerprint unlock](../README.md#fingerprint-unlock) before enabling it. - **FIDO2 Key Unlock** (opt-in, `fidoUnlock`): - Unlock the vault with a FIDO2 authenticator -- a YubiKey or any compliant key with `hmac-secret` -- instead of retyping your master password. - Uses the registration Omarchy's own setup writes (`omarchy setup security fido2` to `/etc/fido2/fido2`), so one registration serves the vault and the system's own authentication prompts alike, and nobody re-enrolls. The setup screen offers that command when no key is registered yet. - One touch asks the key for its `hmac-secret`, which opens the stored password. The key will not produce it without a touch, so unlocking needs the key itself, not just access to the keyring. - Each plugged-in registered key gets its own way in; registrations that ask for the key's PIN (`+pin`, `+verification`) are not used. The key is armed automatically on lock when one is plugged in; the fingerprint reader is the fallback, and the master password field always stays available. - See [FIDO2 key unlock](../README.md#fido2-key-unlock). - **SSH Agent** (opt-in, `sshAgentEnabled`): - Serves the SSH keys in your vault to `ssh`, `git` and `ssh-keygen -Y sign` while the vault is unlocked, from a separate helper process that holds the private keys in memory and drops them on lock, logout, or exit. They are never written to disk and never reach QML. - Every signature shows the key, its fingerprint and the program asking. The default prompt lives in the panel; the opt-in centered popup makes the plugin otherwise disappear until a decision is needed. One approval can cover a whole rebase; live approvals are listed and revocable, and repeated unanswered prompts fall back to a cooldown rather than pestering. - Public keys are projected to files for Git signing, one file per item, public material only. See [SSH Agent](ssh-agent.md). - **Context-Aware Password Suggestions (Active Window / Browser Tab)**: - Reads the active window on open (`hyprctl activewindow -j`, falling back to the `hyprctl clients -j` focus history when the panel itself holds focus). - Recognises the current site from the browser's page title and matches it against each item's URLs and name using Bitwarden-style host and base-domain rules, brand aliases (a `Gmail` tab matches a `google.com` item), and word-boundary matching that ignores public suffixes and generic labels such as `www`, `login`, or `com`. - Places a highlighted **`󰌠 Matches window title: `** banner and pins matching credentials to the top of the list with pre-selection, so pressing Enter immediately copies the right credential. - **Not phishing protection.** Hyprland exposes a window's title, not the tab's address, and a page writes its own title: one titled `github.com` on any domain is matched to a `github.com` item. The banner therefore says what the title matched, never "this site", and the address bar remains the thing to check before pasting. - Only the strongest tier of matches is shown (at most 6), and a title with nothing identifiable in it produces no suggestions rather than a guess. - Standalone desktop apps match on window class; terminals only suggest for remote `ssh`/`mosh`/`sftp` hosts, never for local shells. - **It learns.** Opening or copying an item while a window is active records the window's site (a domain in its title) or app against the item, and it is suggested outright next time -- ahead of every heuristic. Learned suggestions are marked `󰐾` rather than `󰌠`. - **Suggest here / Suggested here** in an item's detail view pins or unpins that item for the current site or app deliberately. A pin is also the only thing that records the title's words, which is what handles sites a title can never match: a portal on `auth.example.xyz` titled `Home - authentik` shares no word with the stored credential, so pin it once and it sticks. An ordinary pick never learns title words: a learned match skips scoring and a page chooses its own title, so a word learned from a pick let any page sharing it (a lookalike) have the real site's login suggested first. Word keys an older version learned from picks are kept in the file but no longer match. - Re-picking a different item retargets what was learned, so a bad association corrects itself the next time you choose. - Associations live in `~/.local/state/qs-bitwarden-cli/associations.json` (mode `600`; `associations@.json` for an account added beside the first) and hold only vault item IDs and the domains, apps and pinned title words they were learned from -- never credentials. The file goes to its writer on standard input (an environment variable is capped at 128 KiB, and a store past that silently stopped being saved). Writes replace a private temporary file atomically, never follow a symlink, and narrow an older permissive file back to `600`; reads validate the schema and discard unknown versions, malformed entries, and keys outside the `domain:`, `app:`, and `word:` namespaces. **Logging out deletes the file after any active writer has exited**, so an in-flight update cannot resurrect the previous account's metadata. It holds no secrets, but between them the domains, app names and timestamps are a record of which sites the account has logins for and when each was last used, in the clear and with no expiry of its own. - **Limitation:** browsers do not publish the active tab URL to Wayland or Hyprland, so the *heuristics* work from the page title. A site whose title mentions neither its name nor its domain (`New Tab`, a bare `Sign in`) cannot be inferred -- teach it once instead. - **Smart Auto-Copy TOTP Flow on Enter**: - Selecting a login item and pressing Enter copies the **Password** to the clipboard and automatically closes the panel, returning focus immediately to your target application so you can paste (Ctrl+V) and submit. - If the item has a TOTP 2FA secret configured, the plugin automatically copies the live 6-digit **TOTP code** to your clipboard after a brief delay (default: 3s) and posts a desktop notification saying so. The notification deliberately does **not** contain the code: a notification daemon keeps history and can render a body over a lock screen, which is no place for a live second factor. The digits are shown in the panel itself, with their countdown. - You can immediately paste the TOTP code into the 2FA prompt without ever reopening or refocusing the plugin! - If you prefer manual progression, pressing Enter or t while the follow-up banner is active also copies the code immediately. - **Every item type is readable, not just logins**: - **Cards** show the cardholder, brand, number, expiry and security code. The number and the code are masked until revealed, and each reveals independently of the other -- an eye is a statement about the field it sits on. - **Identities** show the name, username, company, email and phone, the social security, passport and licence numbers, and the address as a single copyable block rather than seven separate rows. Empty fields are not drawn, so a sparsely filled identity stays short. The three identifiers are masked for the reason a password is, with the difference that these cannot be rotated afterwards. - Both are searchable by what the list shows them as: a card by brand, cardholder or last four digits, an identity by name, email, username or company. Deliberately **not** by the middle of a card number -- a substring search across stored card numbers is not a lookup this box should perform. - **SSH keys** remain public records: the panel lists them, shows the public key and fingerprint, and serves them through the agent, but never draws or writes private material. - **Full Add, Edit & Delete (CRUD) Operations**: - **Create Items (`n` key or `+` button)**: Add new **Logins** (`󰌋`), **Secure Notes** (`󰈐`), **Cards** (`󰿯`) or **Identities** (``). SSH keys are read-only here: generating one would mean handling private key material, which the plugin deliberately keeps out of its process. - **Password Generator**: A full generator screen (g or the `󰌆` button) mirroring the Bitwarden browser extension's options -- password (length, A-Z, a-z, 0-9, special, minimum numbers, minimum special, avoid ambiguous) or passphrase (word count, separator, capitalise, include number), with a live strength meter. Generation comes from Bitwarden's own generator rather than a reimplementation, and the item form's **Generate...** button opens this same screen and fills the password field in on the way back. - **It is fast.** A fresh `bw generate` costs about 2.9 seconds, almost none of it generation: roughly 0.9s is the CLI's Node bootstrap and 2s is Bitwarden's service container starting, and every option toggle paid it again. The panel now starts `bw serve` the first time you open the generator and asks that, which answers in about **2ms**. The server listens on a Unix socket in the private runtime directory (`$XDG_RUNTIME_DIR/qs-bitwarden-cli/generator.sock`, directory mode `700`), not on a loopback port: a port is reachable by every user on the machine, who could ask `/unlock` to guess your master password (no second factor, no lockout) and `/status` for your email and user id. (A logged-out server on an empty data directory would hold no account at all, but Bitwarden CLI 2026.2.0 refuses to serve while logged out.) The server is also started with **no session**, so it is a locked vault, and it is only up while the generator screen is. An answer on the socket is not taken as proof the server is ours: it is probed before we start, and anything already answering means the panel uses `bw generate` for that visit. If our own server later dies, any value it had already supplied is discarded rather than left on screen to be copied, and a `bw` that cannot serve on a socket falls back to `bw generate`. **No request can hold the panel up or fill it.** Every request runs through a managed child process with curl's config disabled, proxies bypassed, a two-second deadline, and a producer-side 64 KB cap. - **Edit Items (`e` key or Edit button)**: Modify titles, credentials, authenticator keys, URLs, notes, and every card and identity field. **Saving changes only what you changed**: a password's edge spaces and a note's trailing newlines are kept, and the website field edits the first website only -- every other website, and each one's match rule, is kept as stored (saving used to replace them all with one entry at the default rule, which widened browser-extension autofill). Enter saves from anywhere in the form, so a long item does not have to be scrolled to the bottom to be committed -- except while a folder, organization or collection picker is open, where Enter belongs to the list being picked from. - **Delete Items (`x` key or Delete button)**: Delete items with confirmation protection. - **Saving and deleting do not hold the panel.** Both cost whatever `bw` costs -- a second or two of CLI startup, vault decryption and a round trip -- and the panel used to spend all of it on a frozen form. The form closes as soon as the command is launched and the list shows the item as it will be, its icon replaced by a spinner until the vault answers, at which point the authoritative version takes its place. An item still being saved cannot be edited or deleted, and a second save waits for the first. If the vault refuses one, the list goes straight back to what the vault actually holds and the message offers to reopen what you typed rather than costing you the edit. - **Bitwarden Send** (Alt+S or the `󰒗` button): - Share a secret through a link that expires on its own, so a credential need not live in a chat log. - Create a text Send with a name, hidden-by-default text, a deletion window (1-31 days), a maximum view count, and an optional password. The access link is copied to your clipboard the moment it is created. - Lists your existing Sends with how long each has left (`in 3 days`, `expired`), views used against the maximum, and whether a password is set. Copy a link or delete a Send from the row. - Keyboard: n new, r refresh, x delete the highlighted Send, Enter copy its link, Esc back. - The Send payload -- which carries the Send password -- is passed to `bw` through the environment, never on the command line. - **Attachments**: - Items that carry files are marked with a `󰏢` paperclip in the list, and the detail view lists each attachment with its name and size. - The list costs nothing: `bw list items` already returns the attachment metadata with the cipher, so the files are on screen the moment the item opens. Only the bytes need the CLI, and only for the file you ask for. - **Save** puts a file in your download directory (`xdg-user-dir DOWNLOAD`, falling back to `~/Downloads`), then offers **Open** and **Show in folder** for it. a saves every attachment on the item; they are fetched one at a time rather than starting a `bw` per file. - Nothing is ever written through whatever already sits at the chosen path: the bytes land in a private staging directory first and the finished file claims its name atomically, so a symlink left in the download folder is stepped around rather than followed, and an existing file is never overwritten -- " (1)", " (2)" and so on go before the extension until the name is free. - A download is bounded before it starts and while it runs: 512 MB per file, 15 minutes, and a check that the disk has room. If the server omits the size, the preflight reserves for the full 512 MB ceiling rather than treating it as an empty file. A transfer that breaks a limit leaves nothing behind. - **A file name out of the vault is treated as hostile.** It is decrypted content that is about to become part of a path, so path separators and control characters are replaced rather than stripped, a leading dot or dash is dropped, and the result is quoted on top of that: `../../.bashrc` saves as `bashrc` in your download directory and nowhere else. Tests run the real script against a stub `bw` to prove it. - **Folders**: - Filter by folder from the bottom filter bar: **All Folders**, **No Folder**, or any specific folder. - Items show their folder inline (`󰉋 Name`) when no folder filter is active. - Assign a folder when creating or editing an item from an expandable list, including clearing an existing assignment, and create a new folder inline without leaving the form. - **Unified Bottom Filter Bar**: - Three identical buttons centred at the bottom -- **Folders**, **Organizations**, **Types** -- each showing its current selection, so the active filters are readable at a glance without opening anything. - Opening one drops the window down like a drawer rather than squeezing the item list, with a pinned header naming the group (and its total when it overflows). - Five options are visible at a time and the rest scroll underneath the pinned header. - Any action outside the drawer closes it -- selecting an item, searching, copying, syncing, locking or opening another screen -- so it never sits over the results. - Fully keyboard driven: f folders, o organizations, t types; ↑/↓ move through the options, Enter applies, Esc closes. The cursor starts on the option already active, so Enter changes nothing by accident. - **Organizations & Collections**: - The item form picks an organization from an expandable list, and reveals that organization's **collections** once one is chosen -- Bitwarden files org-owned items into collections rather than folders, and refuses to save one that is in none. - Collections are a multi-select, since an item can belong to several. A lone collection is pre-selected, and the form says "pick at least one" before the CLI would. - Choosing **My Vault** for an organization item clears both its organization and its collections. - **Multi-Organization & Vault Filtering**: - Automatically queries and displays organizations you belong to. - Organization filter bar: **All Vaults**, **My Vault** (Personal items), or specific shared **Organization**. - Shared items display a prominent `󰓹 Org` tag in the list and detail views. - Choose destination vault (Personal vs. Organization) when creating or editing items. - **Guided First Run — no prerequisites**: - `omarchy plugin add ... --enable` is the entire install. The widget enables into the bar with nothing else installed, wearing a `+` badge, and opens on its setup screen instead of a login form it cannot service. - The dependency probe runs ahead of anything that touches `bw`, so a machine without the CLI never lands on a dead end. - The setup screen watches for the install it launched and moves on to the vault by itself the moment the tools land — no re-check, no restart. - **Setup Wizard & In-Panel Settings**: - Checks the tools Omarchy does not already ship (`bw`, plus fingerprint unlock) in a single probe, marking each required or optional and saying what it is for. Everything else the plugin shells out to comes with Omarchy, so the screen stays one short list rather than a wall of rows that are green on every machine. - A missing **required** tool opens the wizard automatically. **Install** hands off to `omarchy install app`, which surfaces the install in Omarchy's own floating, centred terminal -- the same window every other app install on the system opens. - Fingerprint unlock is Omarchy's job end to end: **Set up** runs `omarchy setup security fingerprint`, which detects the reader, installs `libfprint`/`fprintd`/`usbutils`, enrols a finger, verifies it, and writes the PAM stacks. The row is only drawn on a machine with a reader (`omarchy-hw-fingerprint`), and there is no `pkg add fprintd` button, because installing the package alone leaves the row exactly as red as it was. - Press , or the `󰒓` button for settings, grouped into **General**, **Security** and **SSH Agent**: auto-lock timeout, clipboard clear delay, TOTP auto-copy delay, and every toggle. The section you are reading is named above the list and stays there as you scroll. Maintenance actions sit under their own heading, and the destructive one -- **Remove Plugin Data** -- under a separated **DANGER ZONE**, so the button that clears your keyring entries does not look as safe to press as the one that opens a checklist. A setting whose dependency is missing is shown but inert, with the reason given. - 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 change. Nothing is stored in a second place. - Reachable from a keybind too: `omarchy-shell io.github.elevate08.qs-bitwarden-cli settings` (or `setup`). - **Hardware-Accelerated Performance & Security**: - Virtualized `ListView` with component delegate recycling for instant rendering of large vaults. - Asynchronous search debouncing (50ms) for responsive 0ms typing latency. - **Unlock and password-login startup is prewarmed.** Opening the locked screen, or focusing the password field while logged out, starts the Bitwarden CLI and leaves it waiting on a private mode-`600` FIFO. Submitting the form writes the exact password bytes into that pipe, so most of the CLI's Node and service-container startup has already happened. Closing the panel cancels the waiting process and removes the FIFO. - **Items load before secondary metadata.** After authentication, the first command fetches only the item list. Folders, organizations and the confirming status refresh begin after those items have been parsed and rendered. Short `Loading items...` and `Syncing...` status text exposes the work without adding another screen. - **There is no persistent item cache.** Every authenticated cold load is verified through `bw`; decrypted vault items are never written into the plugin directory or another cache. The speedup comes from moving startup off the submit path and removing unrelated commands from the critical item path. - **Credentials do not reach a command line.** `/proc//cmdline` is world-readable on a default Linux install while `/proc//environ` is not. The session token travels in `BW_SESSION`; direct unlock and email-login passwords move from `BW_PASSWORD` through the private FIFO selected with `--passwordfile`; API key credentials use `BW_CLIENTID` / `BW_CLIENTSECRET`; item, folder, and Send payloads and copied secrets use their own variables. None is interpolated into a command or shell script. The one exception is the two-step login code: `bw` offers no environment option for it, so `--code` puts it in `bw`'s own argv for the length of the login — it is carried in `QSBW_CODE` and expanded there, which at least keeps it out of the wrapping shell. Tests assert that no builder emits `--session` and that no auth command carries a password, client secret or client ID in argv. - **The custom-server field is checked before the master password is sent to it.** `bw config server` takes whatever it is given, and the next thing down that path is your master password, so a plain `http://` address is refused unless it is loopback -- where there is no wire to listen on, and where a local Vaultwarden or an SSH tunnel to one is a normal way to run this. Any scheme that is not `http` or `https` is refused outright. The loopback exemption is anchored and userinfo is stripped before the host is judged, so `http://localhost.evil.com` and `http://localhost@evil.com` are both refused. Backslashes are refused too: WHATWG clients interpret them as path separators, and otherwise `http://evil.example\@localhost` can look local to a lightweight parser while connecting to `evil.example`. - Automatic clipboard clearing after a configurable timeout (default: 30s): the copy is served by `wl-copy --foreground` under `timeout`, detached from the shell, so the clear happens even if the shell restarts, and a newer copy (yours or anyone's) makes that `wl-copy` exit, so the deadline never touches it. Locking or switching accounts clears the clipboard only while it still holds a copy marked sensitive. Copies are marked sensitive so clipboard history skips them. Password and TOTP fallback reads are managed and generation-checked, so a command that finishes after a lock cannot put its result on the clipboard. - **A shell crash leaves no vault in its core dump.** The session key and every item's secrets are held by a separate helper process that turns its own core dumps off and cannot be attached to; the shell holds names, usernames and websites, and only the one item you open. Copies go from the helper to the clipboard, and TOTP codes and search are answered there. The shell's own core dumps are untouched. If the helper is unavailable, the panel falls back to holding the vault itself and says crash protection is off. See [Vault helper](vault-helper.md). - **Master password re-prompt is honoured.** An item marked "Master password re-prompt" in Bitwarden asks for your master password before its password, TOTP, hidden fields or card details are revealed or copied, and before it is edited or deleted. The password is checked against the stored copy, or by `bw` itself when there is none yet, and never on a command line; a confirmation lasts while that item stays open. - Optional session token caching in Linux Secret Service (`secret-tool` / libsecret). Turning it off removes a stored session at once (and at the next start, if it was turned off in `shell.json`), and with it off, unloading the shell while unlocked runs `bw lock`. - **A lock is checked, not assumed.** A failed `bw lock` is retried once and then confirmed with `bw status`, and you are told if the vault is still unlocked in `bw`; the remembered session's keyring clear is verified the same way. Suspending waits for both (up to 4 seconds, inside logind's 5-second limit) instead of a fixed second, since `bw lock` alone takes 1-3 s to start. - **Keyring writes cannot race a lock, logout, or cancelled setup.** Session, PIN, and fingerprint-password stores are generation-stamped; a completion from an old vault or an authentication setup form the user left is cleared instead of recreating a credential that was just removed. - **Locking the vault also discards what was still on its way out of it.** Every read and operation records the vault generation it started under, and the generation moves whenever the vault is locked, logged out of, or unlocked. A late item list, generated password, attachment completion, CRUD result, or newly created Send link is dropped instead of repopulating memory, navigating the locked panel, or copying a secret after the lock. Process output is accepted only after its exit status is known. - **And a lock forgets it as well as refuses it.** Refusing a stale answer still leaves it in the pipe it came down. Quickshell's `StdioCollector` keeps whatever its process last printed for as long as that process is not started again -- `text` is read-only, there is no `clear()`, and nothing drops the buffer when the panel stops reading it -- so every secret that had ever come back through one was still in the shell after the vault locked: the session key from the handoff file and from the keyring, the master password from the PIN and fingerprint lookups, both halves of a login or unlock, the whole item list with each login's password in its raw object, an item detail, a live TOTP. Clearing the properties those were copied into left the originals sitting behind them. The buffer *is* replaced when the process next starts, so a lock now runs a command that prints nothing through every collector that can hold vault data or a credential; anything still mid-read is come back for once it finishes. - **Auto-lock counts the time the machine was asleep.** Qt schedules its timers on the monotonic clock, which Linux stops while the machine is suspended, so a fifteen-minute countdown armed just before the lid closed still had fifteen minutes to run when the lid opened -- a vault left overnight came back exactly as open as it was left. The deadline is kept in wall-clock terms as well and polled every thirty seconds, so waking a suspended machine locks the vault rather than resuming the countdown. Between the monotonic timer and the wall clock, whichever notices first does the locking. - **The vault locks when the screen locks and when the machine suspends.** - **Settings are validated where they are read, not only where they are written.** Nothing validates `shell.json`, and a bad value there can fail open rather than loudly: a non-numeric minute count reaches QML as `NaN`, lands in an integer property as `0`, and `0` is how "never lock" is spelled, while a count past the documented ceiling overflows the timer's 32-bit interval into a negative number that never fires. Each numeric setting is held to the range in the table below, and anything unreadable falls back to its default instead of to zero. Boolean settings accept only actual JSON booleans, so strings such as `"false"` cannot become truthy by JavaScript coercion. - **A remembered session does not survive a reboot.** The login keyring is a file on disk that PAM unlocks again at the next login, so a machine powered off with an unlocked vault used to come back unlocked. Two things stop that. The token is written to libsecret's `session` collection, which the secret service holds in memory and destroys with the login session, so there is nothing on disk to come back; and it is stamped with the kernel's boot id, so a token that does survive -- a secret service with no session collection, a keyring restored from a backup -- no longer matches the running boot and is refused and cleared instead of used. Restarting the shell still keeps you unlocked. Powering the machine off does not. ---