Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

lazybw

lazybw is a terminal interface for your Bitwarden vault. You browse, search, copy and edit items with the keyboard, the way you would move around in Vim or lazygit, and you never leave the terminal.

The vault screen: filters on the left, the item list in the middle and the selected login on the right

It runs on top of the official Bitwarden command-line client, bw. lazybw starts bw serve in the background and talks to it over a private channel, so all encryption and decryption happen in Bitwarden’s own code. lazybw only draws the screen and sends requests.

What you can do with it

  • Log in (email and password with two-step login, single sign-on or an API key), unlock, lock and sync.
  • Browse logins, secure notes, cards, identities and SSH keys by type or by folder, and find any item with fuzzy search.
  • Copy usernames, passwords and verification codes. The clipboard clears itself after 30 seconds.
  • Create and edit items in a form, or open the notes (or the whole item as JSON) in your $EDITOR.
  • Generate passwords and passphrases, check the vault for weak, reused or exposed passwords, and import or export the vault.
  • Change every keybinding, pick one of ten themes or write your own.

Who it is for

lazybw is for people who already live in a terminal and use Bitwarden. If you like Vim motions, you will feel at home within minutes. If you don’t, the arrow keys work too, and ? shows the keys for the screen you are on.

It runs on Linux, locally or over SSH, under X11 or Wayland.

How this book is organised

Getting started covers installation and the first login. Using lazybw goes through the daily tasks one by one. Making it yours explains the configuration file, the keybindings and the themes. The last chapters describe how lazybw protects your data and list every key, option and command.

Installation

Requirements

  • Linux. lazybw works in any terminal emulator, under X11 or Wayland, and over SSH.
  • The official Bitwarden CLI, bw, version 2025.9.0 or newer.
  • A recent Rust toolchain (1.88 or newer) to build lazybw.
  • A Nerd Font in your terminal, for the icons. This one is optional: lazybw can use plain Unicode or ASCII symbols instead.

Install the Bitwarden CLI

Use your distribution’s package if it has one. On Arch Linux:

sudo pacman -S bitwarden-cli

Anywhere else with Node.js installed:

npm install -g @bitwarden/cli

Check the version:

bw --version

lazybw refuses to start with a bw older than 2025.9.0, because it needs the private-socket mode of bw serve that appeared in that release. A few features need newer versions: archived items appear from 2025.10.0, and the vault health report uses bw report password-health from 2026.8.0 (older versions get a local check without the data-breach lookups).

Install lazybw

Each release has static binaries for x86_64 and aarch64 (64-bit ARM), and packages built from them. The binaries have no library dependencies, so they run on any Linux distribution.

Debian and Ubuntu

sudo apt install ./lazybw_1.0.0-1_amd64.deb     # or _arm64.deb

Fedora, openSUSE and RHEL

sudo dnf install ./lazybw-1.0.0-1.x86_64.rpm    # or .aarch64.rpm

Arch Linux

lazybw is in the AUR in two forms: lazybw-bin installs the release binary, and lazybw builds it from source. With an AUR helper such as paru or yay:

paru -S lazybw-bin     # or: paru -S lazybw

Without a helper:

git clone https://aur.archlinux.org/lazybw-bin.git
cd lazybw-bin
makepkg -si

The release also has a ready-made package:

sudo pacman -U lazybw-1.0.0-1-x86_64.pkg.tar.zst

On x86_64 every package pulls in bitwarden-cli from the Arch repositories.

Any other Linux

tar -xzf lazybw-1.0.0-x86_64-unknown-linux-musl.tar.gz
install -Dm755 lazybw-1.0.0-x86_64-unknown-linux-musl/lazybw ~/.local/bin/lazybw

Every release also has a SHA256SUMS file. Check your download with sha256sum --check --ignore-missing SHA256SUMS.

Android (Termux)

The aarch64 binary runs in Termux:

pkg install nodejs
npm install -g @bitwarden/cli
tar -xzf lazybw-1.0.0-aarch64-unknown-linux-musl.tar.gz
install -Dm755 lazybw-1.0.0-aarch64-unknown-linux-musl/lazybw "$PREFIX/bin/lazybw"

Termux has no system clipboard that lazybw can reach directly; it copies through your terminal with OSC 52 instead, which the Termux app supports.

Docker

The image on the GitHub Container Registry includes bw:

docker run -it --rm -v lazybw:/home/lazybw ghcr.io/syscallbrain/lazybw

The lazybw volume keeps your Bitwarden login, the lazybw configuration and its state between runs. Inside a container there is no system clipboard; lazybw copies through your terminal with OSC 52. Run lazybw subcommands by adding them at the end, for example … ghcr.io/syscallbrain/lazybw doctor.

From source

With Rust 1.88 or newer:

cargo install --git https://github.com/SyscallBrain/lazybw lazybw

Or from a clone, which also lets you build the packages yourself (scripts/dist.sh):

git clone https://github.com/SyscallBrain/lazybw
cd lazybw
cargo install --path crates/lazybw

On Arch Linux, the lazybw AUR package does this for you.

Check the setup

lazybw doctor

doctor prints what lazybw found: where bw is and which version, whether you are logged in, which clipboard backend it will use, the terminal size and colour depth, and the paths of the configuration, state and log files. Run it first whenever something does not work.

Where lazybw keeps its files

WhatWhere
Configuration~/.config/lazybw/config.toml
Your themes~/.config/lazybw/themes/*.toml
Log, command history, folder state~/.local/state/lazybw/
Temporary files (exports, $EDITOR buffers)$XDG_RUNTIME_DIR/lazybw/

lazybw follows the XDG variables, so $XDG_CONFIG_HOME and $XDG_STATE_HOME move these if you set them. Your vault itself stays where bw keeps it.

First run

Start lazybw with:

lazybw

The first screen says “Starting Bitwarden CLI…” while lazybw finds bw, checks its version and asks it whether you are logged in. What comes next depends on the answer.

Logging in

If bw has no account yet, you get the login screen. It has two boxes:

  • Server: bitwarden.com (United States), bitwarden.eu (European Union) or your own self-hosted server.
  • Method: email and password, single sign-on, or an API key.

Move with j and k, switch between the boxes with Tab and press Enter to continue.

For email and password, and for single sign-on, lazybw steps aside and runs bw login in the terminal, so you see Bitwarden’s own prompts, including two-step login. When bw login finishes, lazybw comes back and opens the vault. You are not asked for the master password a second time.

For an API key, lazybw asks for the client id and secret in its own form. You will find both in the Bitwarden web vault under Settings, Security, Keys.

Unlocking

When bw already knows your account but the vault is locked, lazybw shows the unlock screen. Type the master password and press Enter.

Ctrl-o logs out instead (after confirming), and Ctrl-c quits. Ctrl-c also quits from the login screen and from the start-up and error screens.

Locking

The vault locks after 15 minutes without a key press, and you can lock it yourself with Space then l. Locking wipes everything lazybw holds in memory: the decrypted items, the undo history, the generator history and the clipboard, if it still holds something lazybw copied.

You can also lock a running lazybw from outside, for example from a screen-locker hook:

pkill -USR1 lazybw

Quitting

Press q on the vault screen, or type :q. lazybw clears the clipboard on the way out if it still holds a value it copied.

A tour of the screen

The vault screen has a bar at the top, panes in the middle and two lines at the bottom.

 ◆ lazybw  1 Vault  2 Generator  3 Health       you@example.com │ unlocked
╭ Filters ──────────╮╭ Logins 198 ──── name ↓ ╮╭ GitHub ──────────── Login ╮
│  ≡ All items  247 ││ ● Amazon               ││ ● GitHub                  │
│  ★ Favorites   12 ││   shop@example.com     ││ Login · Personal          │
│ TYPES             ││▌● GitHub           ★ ◷ ││ USERNAME                  │
│▌ ● Logins     198 ││▌  dev-example          ││ dev-example            yu │
│ FOLDERS           ││ ● GitLab            ◷  ││ PASSWORD                  │
│  ▾ Personal    80 ││   dev@example.com      ││ •••••••••• za reveal · yp │
│      Banking    6 ││                        ││                           │
╰───────────────────╯╰────────────────────────╯╰───────────────────────────╯
 NORMAL  Vault › Logins › GitHub          ⟳ 2m ago │ ⚿ locks 14:32   3/198
 Copied password for "GitHub". Clipboard clears in 30s.

The top bar

On the left are the three tabs: Vault, Generator and Health. Switch with Space followed by 1, 2 or 3, or with gt and gT. On the right you see the account email, the server and whether the vault is unlocked.

The panes

The filters pane lists All items, Favorites, the item types, your folders, No folder and the Trash, each with a count. The middle pane lists the items that match the selected filter. The right pane shows the item under the cursor.

The focused pane has a coloured border. Tab moves the focus to the next pane, and h and l move left and right.

The layout follows the width of the terminal:

WidthLayout
140 columns or moreThree panes, as above.
100 to 139 columnsThe filters become a narrow column of icons (the rail). The folder icon opens a folder picker.
Under 100 columnsOne pane at a time. l or Enter goes one level deeper, h goes back.

Space b hides or shows the filters pane, and you can drag the pane borders with the mouse to resize them.

The bottom lines

The status line starts with the mode (NORMAL, INSERT, VISUAL, COMMAND or SEARCH), followed by where you are. Further right it shows the keys you have typed so far, the clipboard countdown while a copied value is pending, the time of the last sync, when the vault will lock, and your position in the list.

The line under it shows messages, such as “Copied password” or an error from bw. Space m opens the history of all messages from the session.

List markers

Items in the list can carry small markers on the right: ★ for favourites, ◷ for items with a verification code, an icon for passkeys and a lock for items that ask for the master password again. After a health check, a warning sign marks passwords that are weak, reused or exposed.

Moving around

lazybw uses Vim’s keys. If you know Vim, most of this page will be familiar.

Basic movement

KeysMoves
j / k (or the arrow keys)down / up one row
gg / Gto the first / last row
Ctrl-d / Ctrl-uhalf a page down / up
Ctrl-f / Ctrl-ba page down / up
H / M / Lto the top / middle / bottom of the visible rows
zz / zt / zbscroll so the cursor sits in the middle / top / bottom
h / lfocus the pane to the left / right
Tab / Shift-Tabfocus the next / previous pane

A number before a motion repeats it: 5j moves five rows down and 12G goes to the twelfth item. A few actions take a count too, so 3dd moves three items to the trash.

The leader key

Many commands start with the leader key, which is Space unless you change it. Space g opens the generator, Space l locks the vault, and so on. In this book the leader is written as Space.

Which-key

The which-key panel listing every command that can follow Space

Press Space and wait a moment. A panel opens above the status line with every key that can follow, each with a short description. Groups that lead to more keys are marked with a +, such as +folders behind Space f. The panel stays open until you press a key, and Esc cancels.

The same panel appears for the other prefixes: g, z, y and d.

If you type a sequence that does not exist, such as z followed by a key that has no meaning after z, lazybw drops the whole sequence and does nothing. It never runs the second key on its own.

Help

? opens the help for the screen you are on, grouped by context. Press / to search it, and q or Esc to close it.

Mouse

Clicking selects panes, rows and tabs, the wheel scrolls, and dragging a pane border resizes it. Set ui.mouse = false if you would rather the terminal kept the mouse for selecting text.

Redrawing the screen

If something ever leaves garbage on the screen, Ctrl-l repaints all of it.

Finding items

Filters

Pick a filter in the left pane with Enter: all items, favourites, one item type, a folder, items without a folder, or the trash. The list title shows which filter is active and how many items it holds, for example Finanças › Bancos 7.

Space s changes the sort order. It is followed by n (name), m (modified), c (created) or t (type), and Space s r reverses the order.

/ searches inside the current filter. The list narrows as you type, Enter keeps the result and returns to the list, and n and N jump to the next and previous match. Esc clears the search.

The search is fuzzy, so gthb finds GitHub. It looks at the item name first, then the username, the website hosts and the names of custom fields. Set vault.search_notes = true to search inside notes as well.

You can narrow the search with qualifiers:

QualifierMatches
type:login (or l, note, card, identity, ssh)one item type
folder:work/projfolders whose path starts with this; folder:none for items without one
is:fav, is:trash, is:repromptfavourites, trashed items, items that ask for the master password
is:weak, is:reused, is:exposed, is:old, is:insecureresults of the last health check
has:totp, has:passkey, has:fields, has:notes, has:uriitems with that kind of data
uri:google, user:alice, name:banktext inside one field

Put - in front of a word or qualifier to exclude it, and quotes around a phrase with spaces. A few examples:

git -type:note
has:totp folder:work
is:reused uri:google
"home router"

Quick open

Quick open listing Proton Mail and Gmail for the query mail, with a preview of the match

Ctrl-p (or Space /) opens a finder over the whole vault, wherever you are. Type a few letters and act on the match without leaving the finder:

KeysDoes
Enteropen the item
Ctrl-ycopy the password
Ctrl-ucopy the username
Ctrl-tcopy the verification code
Ctrl-eedit the item
Ctrl-n / Ctrl-pnext / previous match

Quick open uses the same search language as /. It leaves trashed items out unless you add is:trash.

Jumping back

lazybw remembers where you have been. Ctrl-o goes back to the previous item or filter and Ctrl-i goes forward again. gf jumps to the folder of the item under the cursor.

Reading and copying secrets

The detail pane

The right pane shows every field of the selected item: username, password, verification code, websites, custom fields, notes and, for cards and identities, their own fields. Passwords and hidden fields show as dots.

Press l or Enter on an item to move into the detail pane. There j and k move from field to field, and the hint on the right of each row tells you the key that acts on it.

Copying

KeysCopies
yythe main field (in the list) or the field under the cursor (in the detail pane)
yuthe username
ypthe password
ytthe current verification code
yUthe first website
ynthe notes

Copying never shows the value on screen. The status line counts down while the value sits on the clipboard, and after 30 seconds lazybw clears the clipboard, but only if it still holds what lazybw put there. If you copied something else in the meantime, lazybw leaves it alone. Change the delay with security.clipboard_timeout.

lazybw uses the system clipboard on X11 and Wayland, falling back to wl-copy, xclip or xsel. Over SSH, without a display, it sends the text to your local terminal with the OSC 52 escape sequence, which most modern terminal emulators accept.

Revealing a value

za shows the hidden field under the cursor, or the password when you are in the list. zR shows every hidden field of the item and zM hides them all again. A revealed value hides itself after 20 seconds (security.reveal_timeout), when you move to another item, and when the terminal loses focus.

Verification codes

If a login has an authenticator key, lazybw computes the six-digit code itself and shows it with a bar that empties as the 30-second window runs out. This works without a Bitwarden Premium subscription, because lazybw never asks the server for the code. yt copies it. Turn the codes off with security.totp = false.

Items that ask for the master password

Bitwarden lets you mark an item so the master password is required before anything secret in it is shown. lazybw honours this flag everywhere: before revealing, copying, editing, opening the password history or generating a new password for such an item, it asks for your master password. A wrong password keeps the dialog open so you can try again; Esc or Ctrl-c cancels.

To skip the question for a while after a correct answer, set security.reprompt_grace, for example to "5m".

Opening a website

gx opens the item’s first website in your browser. In the detail pane it opens the website under the cursor. A website saved without a scheme, such as github.com, opens as https://github.com.

Links that are not web addresses (file:, androidapp:, or a scheme some program registered, like vscode:) ask before opening, because the item may come from a shared collection and the link starts whatever program handles it.

Password history and item info

gh lists the item’s previous passwords with the date each one stopped being used. K shows the item’s details: when it was created, edited and when the password last changed, its id, folder and type.

Editing items

The editor with credentials on the left and websites, custom fields and notes on the right

Creating an item

Press a in the list. A small menu asks for the type: l login, n secure note, c card, i identity or s SSH key. The new item starts in the folder you are looking at.

Opening the editor

e (or i) on an item opens it in the editor. The form replaces the list and detail panes. On a wide terminal it has two columns: credentials and options on the left, websites, custom fields and notes on the right.

The editor works like Vim, with two modes.

In NORMAL mode you move between fields:

  • j and k go down and up. They walk the whole left column first, then the right one.
  • h and l jump to the other column, to the field at the same height.
  • gg and G go to the first and last field.

In INSERT mode you type into the field under the cursor:

  • i starts typing at the cursor, a after it, I at the start and A at the end.
  • cc (or S) clears the field and starts typing.
  • Esc goes back to NORMAL.
  • Tab and Shift-Tab move to the next and previous field without leaving INSERT.

Inside a field you have the usual editing keys: Backspace, Delete, the arrows, Home, End, Ctrl-w to delete a word and Ctrl-u to delete to the start. Pasting works as well.

Fields with choices

Some fields offer a fixed set of values, like the folder, a card’s brand or a website’s match type. Ctrl-a and Ctrl-x step through the choices, and Enter opens a list to pick from. Checkboxes such as Favorite toggle with Enter.

Lists of websites and custom fields

On a website or custom field, o adds a new one below and O above, and dd removes it. The “+ Add website” and “+ Add custom field” rows do the same with Enter.

Generating a password

Ctrl-g opens the password generator over the form. Adjust the options as on the generator tab, press r for another password, then Enter to put it in the password field, or Esc to cancel. If the cursor is on another hidden field, the password goes there instead.

Using your own editor

Ctrl-e opens the notes in $VISUAL or $EDITOR (or the command in editor.command). Save and quit the editor to bring the text back into the form. On an SSH key’s private key field, Ctrl-e edits the key instead, and lazybw checks that what you saved is a valid key.

gE opens the whole item as JSON, for changes the form cannot make.

The text is written to a temporary file in $XDG_RUNTIME_DIR/lazybw, readable only by you, and lazybw overwrites and deletes it as soon as the editor exits. For Vim, Neovim, Emacs and micro, lazybw also switches off swap, undo, backup and history files, so no copy of the secret is left elsewhere on disk.

Saving and closing

KeysDoes
Ctrl-s or :wsave and stay in the editor
ZZ or :wqsave and close
q, Esc or :qclose; asks first if there are unsaved changes
ZQ or :q!close and throw the changes away

When there are unsaved changes, closing asks “Save changes to …?”. Answer y to save, n to discard or c to stay in the editor. The title shows [+] while the item has unsaved changes.

u undoes the last change and Ctrl-r redoes it. As in Vim, everything you type between i and Esc counts as one change. When you undo back to the last saved state, the [+] disappears. :e! reloads the item as it is on the server.

Each save is also one step in the vault’s undo history: after closing the editor, u in the list puts the item back as it was before the save.

If someone changes the same item elsewhere (in the browser extension, for instance) while you edit it, lazybw shows a banner. Saving then keeps your version.

Folders

Bitwarden folders can be nested by naming them with a slash, such as Finanças/Bancos. lazybw shows them as a tree in the filters pane.

Opening and closing folders

A folder that has subfolders has an arrow in front of it: ▾ when it is open and ▸ when it is closed. In the filters pane:

KeysDoes
l or →open a closed folder; on an open folder or one without subfolders, show its items
h or ←close an open folder; on a subfolder, go up to its parent
Entershow the folder’s items
zaopen or close the folder under the cursor
zR / zMopen / close every folder

Clicking the arrow with the mouse opens or closes the folder as well.

lazybw remembers which folders you closed, also after you quit. It stores them by their Bitwarden id in ~/.local/state/lazybw/folds.json. Folder names are encrypted in your vault, so lazybw never writes them to that file. Since the id stays the same when you rename a folder, a closed folder stays closed after a rename too.

Going to a folder

Space f f opens a list of all folders. Type to filter it and press Enter to show that folder’s items. “No folder” at the top shows the items that are not in any folder.

gf on an item jumps to its folder.

Moving items

m opens a folder list for the item under the cursor. Pick a folder, or “No folder” to take the item out of its current one. To move several items at once, select them first (see bulk actions).

The same list has “+ Create folder…” at the top, which asks for a name and creates the folder.

Creating, renaming and deleting folders

KeysIn the filters paneAnywhere
New folderaSpace f n
RenamerSpace f r
DeleteddSpace f d

Renaming a folder that has subfolders offers to rename the subfolders too, so Work/Projects follows when Work becomes Job. Deleting a folder moves its items to No folder; the dialog asks whether to delete the subfolders as well. All three can be undone with u.

Trash, undo and bulk actions

The trash

dd moves the item under the cursor to the trash. Trashed items stay in Bitwarden’s trash for 30 days, where you can bring them back.

Select Trash in the filters pane (or type :trash) to see them. There:

KeysDoes
rrestore the item
Ddelete it permanently
Xempty the whole trash

Neither can be undone, so lazybw asks first. For emptying the trash you confirm by typing empty.

Undo and redo

u undoes the last change to the vault and Ctrl-r redoes it. This covers creating, editing, trashing, restoring, favouriting and moving items, and creating, renaming and deleting folders. A message tells you what was undone, for example “Undone: moved 3 items to Trash”.

lazybw keeps the last 100 changes of the session. The history is cleared when the vault locks. Permanent deletion, emptying the trash, import and export cannot be undone.

One detail about passwords: if you undo a password change, the item gets the old password back, but Bitwarden’s password history keeps the newer one.

Working on several items

There are two ways to act on more than one item.

x marks or unmarks the item under the cursor. Mark as many as you like, anywhere in the list, and the next action applies to all of them.

V starts visual mode, as in Vim. Moving with j, k, G and the rest extends the selection. Then:

KeysDoes to the selection
dmove to the trash
mmove to a folder
*make favourites (or remove them, if all already are)
D / rdelete permanently / restore (in the trash)
Esc or Vleave visual mode

A bulk action is one step in the undo history, so one u undoes all of it.

Favourites and copies

* adds the item to your favourites or removes it. Y copies the whole item and p creates a new item from that copy, which is a quick way to make a similar login.

Password generator

The generator tab with a password, its strength, the options and the session history

The second tab (Space 2, or Space g) generates passwords and passphrases with Bitwarden’s own generator.

Options

Tab switches between Password and Passphrase. Move between the options with j and k:

  • A password has a length (5 to 128) and four character sets: upper case, lower case, digits and symbols. You can ask for a minimum number of digits and symbols, and leave out characters that are easy to confuse (l, 1, I, O, 0).
  • A passphrase has a number of words (3 to 20), a separator, and options to capitalise the words and add a number.

h and l (or - and +) change numbers and choices, and x turns a checkbox on or off. Every change makes a new password. r makes another one with the same options.

The strength bar under the password estimates how hard it would be to guess.

Using the password

yy copies it. Enter puts it into the login you have selected in the vault, after asking you to confirm. Bitwarden keeps the old password in the item’s password history (the last five).

To generate a password while editing an item, press Ctrl-g in the editor (see Editing items).

History

The right pane lists the passwords generated in this session, newest first. The list exists only in memory and is wiped when the vault locks or lazybw exits.

Defaults

The [generator] section of the configuration sets the options lazybw starts with. Changes you make on the tab last until you quit.

From the command line, :gen 32 generates and copies a 32-character password, and :gen phrase 6 a six-word passphrase.

Vault health

The health tab with a score, five category cards and the exposed passwords

The third tab (Space 3, or Space h) checks your logins and tells you which passwords to change first.

The report

At the top is a password score from 0 to 100 and, next to it, the score you would get after fixing the exposed and reused passwords. The score sums up the logins lazybw checked; it is not a rating of your whole account. Under it are five categories (cards on a wide terminal, a compact row on a narrow one):

CategoryMeans
Exposedthe password appears in known data breaches
Reusedthe same password is used by more than one item
Weakthe password is easy to guess
Oldthe password has not changed for a year (health.old_password_age)
No HTTPSa website of the login uses plain http://

Tab and Shift-Tab move between the cards, and the list below shows the logins in that category, most serious first. For exposed passwords it shows how many times each one was seen in breaches.

KeysDoes
Enteropen the item in the vault
eedit the item
gpgenerate a new password for it and save it in the vault (after confirming)
rrun the check again

gp only changes the password stored in the vault. Change it on the website as well, or the account keeps the old one.

After a check, the vault list marks affected items with a warning sign, and you can search for them with is:weak, is:reused, is:exposed, is:old and is:insecure.

How the breach check works

With bw 2026.8.0 or newer, lazybw runs bw report password-health. To find exposed passwords, bw asks the Have I Been Pwned service using k-anonymity: it sends only the first five characters of each password’s SHA-1 hash and compares the answers locally. Your passwords never leave your machine.

If you would rather not contact Have I Been Pwned at all, set health.check_exposed = false. The Exposed card then shows a dash.

With an older bw, lazybw does the other checks itself and skips the breach lookup.

Import and export

Export

Space e (or :export) opens the export wizard.

  1. Choose the format and where to write the file. The wizard suggests the password-protected format, the safe choice for a backup. Tab completes the path.
  2. For a password-protected file, choose its password and type it again. A strength bar shows how good it is.
  3. Confirm with your master password.

The formats:

FormatNotes
JSONreadable, every field, not encrypted
JSON encrypted with a passwordfor backups; any Bitwarden account can import it with the password
JSON encrypted with your accountonly this Bitwarden account can import it
CSVlogins and notes only, not encrypted

The file is created readable only by you (mode 600). Plain JSON and CSV exports hold every password in clear text, and the wizard says so before writing. Keep such a file on an encrypted disk and delete it when you are done.

You can skip the first step with :export json ~/backup.json, or with encrypted, account or csv as the format.

Import

Space i (or :import) opens the import wizard.

  1. Pick the format. The list comes from bw import --formats, with the Bitwarden formats first. Type to filter it: 1password, keepass, lastpass and many others are there.
  2. Type the path of the file. Tab completes it. A password-protected Bitwarden file also asks for its password.
  3. For Bitwarden JSON files, lazybw shows a preview before importing.

The preview counts the items by type, lists the folders the import will create, and finds items you already have. An item counts as a duplicate when it has the same type and name and, for logins, the same username and the same site. Duplicates are skipped by default. In the preview:

KeysDoes
j / kmove through the duplicates
x or Spacekeep this duplicate (import it), or skip it again
askip all duplicates, or none
sturn skipping off (import everything) or on
Enterimport

When skipping, lazybw writes a filtered copy of the file to $XDG_RUNTIME_DIR/lazybw, imports that, and overwrites and deletes the copy straight away. Folders of the remaining items are kept.

After the import, lazybw syncs, shows “Imported N items. Skipped M duplicate(s).”, and lists all items sorted by creation date, so the new ones are easy to check.

Imports cannot be undone, so it is worth exporting a backup first.

:import bitwardenjson ~/file.json skips straight to the file step.

The command line

: opens a command line at the bottom of the screen, as in Vim. Tab completes commands and their arguments (themes, folders, item types, settings, file paths), and the up and down arrows go through earlier commands. lazybw keeps this history between sessions.

The most useful commands:

CommandDoes
:q, :q!quit (in the editor: close the item, or discard it)
:qa, :qa!quit, even from the editor
:w, :wqsave, save and close (in the editor)
:new loginnew item of a type (login, note, card, identity, ssh)
:edit jsonedit the item as JSON in $EDITOR
:copy passwordcopy a field of the selected item
:move Workmove the selected item to a folder
:folder new Taxescreate a folder (also rename and delete)
:gen 32, :gen phrase 6generate a password or passphrase and copy it
:sort modified, :sort name!change the sort order (! reverses it)
:theme nordswitch theme
:set theme=nordchange a setting; :set animations! toggles one
:sync, :sync!sync, or force a full sync
:lock, :logoutlock the vault, log out
:export, :importopen the wizards
:health, :trash, :empty-trashopen the health tab, the trash, empty the trash
:12go to the twelfth item
:doctorshow the same diagnostics as lazybw doctor

The default keybindings page has the complete list.

Configuration

lazybw reads ~/.config/lazybw/config.toml. The file is optional: without it, lazybw uses the defaults listed in Default configuration. Put in it only the options you want to change.

[ui]
theme = "catppuccin-mocha"
icons = "unicode"

[security]
clipboard_timeout = "20s"
auto_lock = "10m"

[vault]
sort = "modified"

Durations are written as "500ms", "30s", "15m", "2h" or "365d". Options that can be switched off also accept "never".

Changing settings from inside lazybw

The settings screen with appearance and security options

Space , opens the settings screen. Move with j and k, change a value with h and l (or Enter), and reset it to the default with r. The line at the bottom explains the selected option, and a ● marks the ones you have changed from the default. Changes take effect at once and are written back to config.toml, keeping your comments and the order of the file.

The same works from the command line:

:set theme=nord            set a value
:set animations!           toggle a true/false option
:set clipboard_timeout?    show the current value
:set animations&           back to the default

Short names work when they are unambiguous. The full name, such as ui.animations, always works.

Checking the file

lazybw config --check          # validate config.toml
lazybw config --path           # print where it is
lazybw config --print-default  # print the full default file

lazybw also checks the file on start. A mistake, such as a wrong value or a typo in a key binding, opens an error screen that names the file, the line and what is wrong. Unknown keys only produce a warning.

The options most people change

OptionDefaultWhat it does
ui.theme"tokyonight-night"colour theme (see Themes and icons)
ui.icons"nerdfont"nerdfont, unicode or ascii
ui.animationstruesmall fades and slides when panes and popups open
ui.list_density"comfortable"two lines per item, or compact for one
ui.mousetruemouse support
security.clipboard_timeout"30s"when copied values are cleared
security.auto_lock"15m"lock after this long without a key press
security.reveal_timeout"20s"when revealed values hide again
security.reprompt_grace"0s"how long a correct master password re-prompt counts
vault.sync_interval"10m"background sync
vault.sort"name"name, modified, created or type
vault.search_notesfalselet search look inside notes
vault.confirm_trashfalseask before dd moves an item to the trash
editor.command""editor for Ctrl-e; empty means $VISUAL, then $EDITOR, then vi
keys.leader"<Space>"the leader key
keys.timeoutlen"1000ms"how long lazybw waits to see if a sequence continues

Using a different bw

lazybw looks for bw in bw.path first, then in the LAZYBW_BW environment variable, then on your PATH. Set bw.path if you keep several versions.

By default lazybw ignores a BW_SESSION variable in the environment and asks you to unlock. Set bw.inherit_session = true to reuse an existing session instead.

Keybindings

Every key in lazybw can be changed. The full list of defaults is in Default keybindings.

How bindings are organised

Keys belong to a context: global, sidebar, list, detail, editor, insert, generator, health and a few more for popups. lazybw looks in the most specific context first, so dd can mean “move to trash” in the list and “delete folder” in the filters pane.

Keys are written in Vim notation:

NotationKey
a, G, ?a printable key
<CR>, <Esc>, <Tab>, <BS>, <Del>, <Space>named keys
<Up>, <PageDown>, <F5>arrows, paging, function keys
<C-p>, <A-x>, <S-Tab>Ctrl, Alt and Shift combinations
gg, <leader>fnsequences

In the configuration file

[keys]
leader = "<Space>"

[keys.global]
"<leader>tt" = "theme.set tokyonight-storm"

[keys.list]
"dd" = "noop"              # remove a default binding
"<Del>" = "item.trash"     # add another key for an action
"yl" = "item.copy uri"     # an action with an argument
"<leader>w" = ":sync!"     # run a command-line command

A binding replaces the default with the same keys in the same context, and "noop" removes one. The action keeps its other keys. With keys.defaults = false, lazybw starts from an empty keymap and uses only yours.

[keys.labels] sets the text which-key shows for a prefix:

[keys.labels]
"<leader>x" = "+extras"

With the keybinding editor

Space k opens a table of every action with its keys.

KeysDoes
Tab / Shift-Tabswitch context
/filter by description, action or key
Enter or crebind: press the new keys, then Enter
aadd another key to the action
ddremove a key
r / Rreset the action / the whole context
Ctrl-s or :wsave

Changed rows have a dot in front. Saving writes only your differences from the defaults into config.toml.

If the keys you pick are already taken in that context, lazybw tells you which action uses them and asks before replacing it. To bind Enter, Backspace or Escape, edit the file, because the editor uses those keys itself.

For one session

:map list gm item.move adds a binding until you quit, and :unmap list gm removes it. :map on its own lists all bindings.

Checking your bindings

lazybw keys                  # every binding, grouped by context
lazybw keys --context list   # only one context
lazybw keys --defaults       # the built-in defaults, ignoring your file
lazybw keys --check          # validate; exits with an error code on mistakes

--check catches unknown actions (and suggests the closest name), actions used in a context where they make no sense, and keys that would make text impossible to type.

Terminals and special keys

Some terminals cannot tell Ctrl-i from Tab, Ctrl-m from Enter or Ctrl-[ from Esc. lazybw turns on the keyboard protocol that separates them where the terminal supports it: kitty, foot, WezTerm, Ghostty and recent Alacritty. Inside tmux, add set -g extended-keys on to tmux.conf.

Moving in pickers and lists

Every picker (folder, item type, theme, format) and every list in a popup moves with the arrows or with Ctrl-n (next) and Ctrl-p (previous), and so do the settings screen, the keybinding editor and the help. In the pickers, quick open and the command line, letters are typed into the filter, so those keys are the way to move. The picker, quick open and command line keys can be rebound under [keys.picker], [keys.quick_open] and [keys.cmdline].

Themes and icons

Built-in themes

The vault in the tokyonight-day light theme
ThemeStyle
tokyonight-night (default)dark
tokyonight-stormdark
tokyonight-moondark
tokyonight-daylight
catppuccin-mochadark
gruvbox-darkdark
norddark
rose-pinedark
kanagawa-wavedark
terminaluses your terminal’s own colours

Space t opens a picker with the built-in themes and your own. The screen changes to each theme as you move through the list; Enter keeps it and Esc goes back to the one you had. :theme followed by Tab previews the same way. :theme nord switches directly, and ui.theme in the configuration sets the theme lazybw starts with.

lazybw themes lists them, and lazybw themes --preview nord prints a theme’s colours.

With ui.transparent = true, lazybw leaves the background to the terminal, which suits terminals with a background image or blur. Popups keep a solid background so they stay readable.

Your own theme

Put a file in ~/.config/lazybw/themes/, for example my-night.toml:

name = "my-night"
extends = "tokyonight-night"   # start from an existing theme
appearance = "dark"            # dark or light

[palette]
accent = "#bb9af7"

[tokens]
"border.focus" = "magenta"
"match" = "#ff9e64"

Then set ui.theme = "my-night". lazybw reloads the theme when you save the file, so you can tweak it with lazybw open.

A theme has two layers:

  • The palette holds named colours: bg, bg_dark, bg_highlight, bg_visual, border, fg, fg_dark, comment, dark5, accent, blue, cyan, magenta, purple, orange, yellow, green, teal, red and shadow.
  • The tokens say which colour each part of the interface uses. A token can point to a palette name, a #rrggbb colour, an ANSI colour number, or reset for the terminal’s default.

Useful tokens:

TokenColours
bg, bg.bar, bg.floatmain background, top and bottom bars, popups
selection, bg.highlightthe selected row, highlighted rows and segments
border, border.focuspane borders, the focused one
fg, fg.alt, fg.muted, fg.subtletext from strongest to faintest
accenttitles, focus and the selection bar
ok, info, caution, warn, errormessages and states
type.login, type.card, type.identity, type.note, type.sshitem icons
match, link, fav, dirtysearch matches, websites, favourites, [+]
mode.normal, mode.insert, mode.visual, mode.commandthe mode label

The built-in themes are in crates/lazybw/assets/themes/ in the repository and make good starting points.

On terminals without 24-bit colour, lazybw converts every colour to the closest of the 256 standard ones. If colours look wrong, check that COLORTERM is set to truecolor in a terminal that supports it.

Icons

ui.icons chooses the symbols:

  • nerdfont (default) uses icons from a Nerd Font. Install one and select it in your terminal.
  • unicode uses ordinary symbols such as ●, ★ and ▸, which most fonts have.
  • ascii uses only plain characters, for the most limited terminals.

How lazybw keeps secrets safe

lazybw sees every secret you open, so it handles them with care at each step: in memory, on disk, on the clipboard and in the terminal.

Bitwarden does the cryptography

lazybw contains no Bitwarden cryptography. It starts the official bw serve and sends it requests; bw decrypts and encrypts. lazybw itself never makes network connections. Everything goes through bw, which talks to your Bitwarden server, and, for the breach check, to Have I Been Pwned.

A private channel to bw

bw serve normally listens on a TCP port, which any program on the machine could connect to. lazybw starts it on one end of a socket pair instead, a private channel that exists only between the two processes. No port is open and no socket file exists on disk. You can check with ss -tlpn and ss -xlpn while lazybw runs.

When lazybw exits, crashes or is killed, bw serve stops too: the kernel sends it a signal when its parent dies.

Nothing secret on the command line

The master password, the session key and decrypted values never appear in process arguments, where other users could see them with ps. They travel through environment variables of the child process or through the private channel.

Secrets in memory

  • Decrypted values are kept in a wrapper that overwrites the memory when the value is dropped, and prints as *** if it ever ends up in a log.
  • What you type into password fields (unlock, re-prompt, export passwords, API secret) lives in a buffer that is wiped as you edit and when it is dropped.
  • Locking drops the decrypted vault, the undo history, the generator history, search results and copies of items, and clears the clipboard if it still holds a value lazybw copied.
  • lazybw marks its process as not dumpable and turns off core dumps, so a crash does not write the vault to a core file and other processes of the same user cannot read its memory.

Files on disk

  • Files that can hold secrets are created with mode 600 (readable only by you), and lazybw starts with a umask of 077.
  • Temporary files, such as notes opened in $EDITOR or a filtered import, live in $XDG_RUNTIME_DIR/lazybw, which is in memory on most systems. They are overwritten and deleted as soon as they are no longer needed.
  • For Vim, Neovim, Emacs and micro, lazybw switches off swap, undo, backup and history files, so the editor does not keep its own copy of the text.
  • The folder state file stores folder ids, never names.
  • The log (~/.local/state/lazybw/lazybw.log) never contains requests, responses or secret values.

The terminal

While $EDITOR or bw login runs, lazybw stops reading the keyboard, so what you type there, including the master password, goes only to that program.

The clipboard

Copied values are cleared after 30 seconds, on lock and on exit, but only if the clipboard still holds the value lazybw put there. Clipboard managers such as cliphist or Klipper may keep their own history. Check their settings if that matters to you.

Re-prompt and reveal

Items marked “master password re-prompt” in Bitwarden ask for the master password before any secret in them is shown, copied or changed. Revealed values hide again after 20 seconds, when you move to another item, and when the terminal window loses focus.

Limits

These protections narrow what can leak; they do not make a compromised machine safe. In particular:

  • While the vault is unlocked, decrypted data is in memory: in bw serve (a Node.js process lazybw does not control), in lazybw’s cache, and in the text drawn on the screen. Zeroizing removes lazybw’s copies when they are dropped; it cannot reach copies made by other programs or by the terminal.
  • With the clipboard over OSC 52 (over SSH, in a container), lazybw cannot read the clipboard back, so it clears it when the timeout ends even if you copied something else in the meantime. Clipboard managers and terminals may keep their own history.
  • Editors other than Vim, Neovim, Emacs and micro get no special options; check that yours does not keep backups or swap files of the notes you edit.
  • bw.transport = "unix" puts the socket in lazybw’s private runtime directory instead of using a socket pair.
  • lazybw protects against other users and programs reading its files; it cannot protect against someone with your account or root on the machine.

Reporting a problem

If you find a security problem, please report it privately through GitHub’s security advisories for the repository instead of opening a public issue.

Troubleshooting

Start with:

lazybw doctor

It shows what lazybw sees: the bw path and version, the login state, the clipboard backend, the terminal and the file paths. The log is in ~/.local/state/lazybw/lazybw.log, and Space m shows the messages of the current session.

lazybw says bw was not found or is too old

Install the Bitwarden CLI as described in Installation and check bw --version. If bw is not on your PATH, set bw.path in the configuration or the LAZYBW_BW environment variable.

Colours look grey or wrong

lazybw uses 24-bit colour when the terminal says it supports it. Check that echo $COLORTERM prints truecolor. Over SSH and inside tmux this variable is often lost; set it in your shell profile, or in tmux with set -as terminal-features ",*:RGB". The terminal theme uses your terminal’s own palette and works everywhere.

Icons show as boxes or question marks

Your terminal font has no Nerd Font icons. Install a Nerd Font and select it in the terminal, or set ui.icons = "unicode".

Some key combinations do nothing

Many terminals send the same code for Ctrl-i and Tab, Ctrl-m and Enter, or Ctrl-[ and Esc. kitty, foot, WezTerm, Ghostty and recent Alacritty can tell them apart, and lazybw switches that on automatically. In tmux, add set -g extended-keys on. lazybw keys --check warns about bindings your terminal cannot distinguish.

Copying does nothing

lazybw doctor shows the clipboard backend. Under Wayland, install wl-clipboard; under X11, xclip or xsel work as fallbacks. Over SSH, lazybw uses OSC 52, which your local terminal must allow (in tmux: set -g set-clipboard on).

The screen shows leftover characters

Press Ctrl-l to redraw everything. If it keeps happening, please open an issue with your terminal, its version and what you were doing.

Sync errors or “Vault is locked”

The session may have expired on the server. Lock with Space l and unlock again. If that fails, log out with :logout and log in once more.

Reporting a bug

Open an issue on GitHub with the output of lazybw doctor, the last lines of the log and the steps that lead to the problem. Neither contains passwords or vault data, though the doctor output includes your email address, which you may want to remove.

Contributing

Bug reports, ideas and pull requests are welcome on GitHub.

Building and testing

git clone https://github.com/SyscallBrain/lazybw
cd lazybw
cargo build
scripts/check.sh

scripts/check.sh runs cargo fmt --check, Clippy with warnings as errors and the whole test suite. A pull request should pass it.

The tests never touch a real vault. crates/fake-bw is a stand-in for the Bitwarden CLI that keeps its vault in a JSON file and records every request, so tests can check exactly what lazybw sent. The screens are covered by snapshot tests with insta: after an intended change to the interface, review and accept the new snapshots with cargo insta review, or run the tests with INSTA_UPDATE=always.

How the code is organised

lazybw follows The Elm Architecture. All state lives in one AppState. Input, timers and answers from bw become messages; a pure update function changes the state and returns effects (send a request to bw, copy to the clipboard, open $EDITOR), and the main loop carries the effects out. Drawing reads the state and changes nothing.

PathHolds
crates/lazybw/src/app/state, messages, effects, and update/ split by area
crates/lazybw/src/ui/drawing: widgets.rs and one file per group of screens in screens/
crates/lazybw/src/bw/finding bw, starting bw serve, the HTTP client and the models
crates/lazybw/src/vault/the item cache, filters, folders, search and health checks
crates/lazybw/src/input/the key dispatcher, line editing and secret input
crates/lazybw/src/config/configuration, keymap and settings
crates/lazybw/src/theme/themes and colour handling

The docs/ folder has the longer design notes: design.md for the screens, keymap.md for the keymap model, bw-integration.md for everything about talking to bw, and decisions.md for the reasons behind the main choices.

This book

The book lives in book/ and is built with mdBook:

cargo install mdbook
mdbook serve book --open

The screenshots in it come from cargo run -p lazybw --example screenshots, and the logo from python3 scripts/make-logo.py.

Default keybindings

Every binding below can be changed; see Keybindings. <leader> is Space unless you set keys.leader.

Key notation

Canonical notation is Vim’s. Parsing is case-insensitive for names inside <> and case-sensitive for plain characters.

FormExamplesNotes
Printable characterj, G, *, /, ?, :G is Shift+g; never write <S-g> (normalised to G)
Named keys<CR> (<Enter>, <Return>), <Esc>, <Tab>, <S-Tab>, <BS>, <Del>, <Space>, <Up>, <Down>, <Left>, <Right>, <Home>, <End>, <PageUp>, <PageDown>, <Insert>, <F1>…<F12>, <lt> (literal <), <Bar> (``)
Modifiers<C-d>, <A-x> (= <M-x>), <C-A-x>, <S-Up>, <C-S-x><C-S-…> needs keyboard enhancement (§7)
Leader<leader>expands to keys.leader
Sequencesgg, <leader>fn, <C-w>h, zRconcatenation

Equivalences applied at parse time: <C-I> = <C-i>; <S-a> = A; <Return> = <CR>; <M-x> = <A-x>. Without keyboard enhancement, also <C-i> = <Tab>, <C-m> = <CR>, <C-[> = <Esc>, <C-h> = <BS>.

Display (hints, which-key, help, statusline): Ctrl-d, Alt-x, Shift-Tab, Enter, Esc, Space; plain characters are joined (gg, yp), named keys are separated by spaces (Space f n, Ctrl-w h).

Bindings by context

global

KeysAction
qapp.quit
<Esc> <C-c>app.cancel
?app.help
:cmdline.open
<C-p>quick_open
<C-l>app.redraw
h <C-w>h <Left>focus.left
l <C-w>l <Right>focus.right
<Tab> / <S-Tab>focus.next / focus.prev
gt / gTtab.next / tab.prev
<leader>1 <leader>2 <leader>3tab.goto 1/2/3 (Vault, Generator, Health)
/ / n / Nsearch.start / search.next / search.prev
Rvault.sync
<C-o> / <C-i>jump.back / jump.forward
<leader>/quick_open
<leader>nitem.new
<leader>g / <leader>hgenerator.open / health.open
<leader>e / <leader>ivault.export / vault.import
<leader>Svault.sync force
<leader>lvault.lock
<leader>ttheme.pick
<leader>blayout.toggle_sidebar
<leader>dlist.toggle_density
<leader>mapp.messages
<leader>,app.settings
<leader>kapp.keymap
<leader>qapp.quit
<leader>fn / <leader>fr / <leader>fd / <leader>fffolder.new / folder.rename / folder.delete / folder.jump
<leader>sn / <leader>sm / <leader>sc / <leader>st / <leader>srsort by name / modified / created / type, sort.reverse

Which-key group labels: <leader>f “+folders”, <leader>s “+sort”, g “+go”, z “+folds and reveal”, y “+copy”, d “+delete”, <C-w> “+window”.

Movement (shared by sidebar, list, detail, visual, editor, generator, health, settings, keymap_editor, help)

KeysAction
j <Down> / k <Up>nav.down / nav.up
gg / Gnav.top / nav.bottom
<C-d> / <C-u>nav.half_down / nav.half_up
<C-f> <PageDown> / <C-b> <PageUp>nav.page_down / nav.page_up
H / M / Lscreen top / middle / bottom (not in detail/editor)
zz / zt / zbview.center / view.top / view.bottom
KeysAction
<CR>filter.apply
l <Right>fold.expand_or_apply (open a closed folder, else show its items)
h <Left>fold.collapse_or_parent (close an open folder, else go to its parent)
za / zo / zc / zR / zMfold.toggle / fold.open / fold.close / fold.open_all / fold.close_all
a / r / ddfolder.new / folder.rename / folder.delete
u / <C-r>undo / redo

list

KeysAction
<CR> litem.open
yyitem.copy primary
yu / yp / yt / yU / ynitem.copy username/password/totp/uri/notes
aitem.new (type picker)
e iitem.edit
gEitem.edit_json
dditem.trash
Ditem.delete
ritem.restore (Trash filter)
Xtrash.empty (Trash filter)
u / <C-r>undo / redo
*item.favorite
mitem.move
Y / pitem.yank / item.paste_clone
gxitem.open_uri
ghitem.history
gpitem.generate_password
gfitem.goto_folder
Kitem.info
za / zR / zMitem.reveal_toggle / item.reveal_all / item.hide_all
Vvisual.start
xmark.toggle

detail

KeysAction
j / knext / previous field (movement)
yyitem.copy_field
yu / yp / yt / yU / ynas in list
za / zR / zMitem.reveal_toggle / item.reveal_all / item.hide_all
i / ccitem.edit_field / item.change_field
eitem.edit
gxitem.open_uri (the URI under the cursor)
gh / gp / gf / K / * / mas in list
u / <C-r>undo / redo

visual

KeysAction
j k gg G …extend the range
xmark.toggle
ditem.trash (selection)
D / ritem.delete / item.restore (Trash filter)
mitem.move (selection)
*item.favorite (sets all to favorite unless all already are, then removes)
<Esc> Vvisual.exit

editor (NORMAL)

KeysAction
j <Down> <Tab> / k <Up> <S-Tab>editor.next_field / editor.prev_field
gg / Geditor.first_field / editor.last_field
i / a / I / Ainsert / append / insert at start / append at end
cc Seditor.change_field
o / Oeditor.row_add_below / editor.row_add_above
ddeditor.row_delete
<C-a> / <C-x>editor.cycle_next / editor.cycle_prev
<CR>editor.activate
zaitem.reveal_toggle
<C-g>editor.generate
<C-e>editor.external
gEitem.edit_json
u / <C-r>editor.undo / editor.redo
<C-s>editor.save
ZZ / ZQeditor.save_close / editor.discard
qeditor.close
h / leditor.column_left / editor.column_right

insert

KeysAction
<Esc> <C-[> <C-c>editor.normal
<Tab> <CR> / <S-Tab>editor.next_field / editor.prev_field (stay in INSERT)
<C-a> / <C-x>editor.cycle_next / editor.cycle_prev (on option fields)
<C-g>editor.generate
<C-e>editor.external
<C-s>editor.save

Fixed line-editing keys in INSERT (not remappable): <BS>, <Del>, <Left>, <Right>, <Home>, <End>, <C-w> (delete word), <C-u> (delete to start), <C-h> (= <BS>), bracketed paste.

notes

edtui provides Vim NORMAL/INSERT/VISUAL inside the notes field. lazybw binds only: <Tab>/<S-Tab> in NORMAL to leave the field, <C-e> external editor, <C-s> save, and : to open the lazybw command line (so :w, :wq, :q! keep working).

generator

KeysAction
rgenerator.regenerate
yygenerator.copy
<Tab>generator.toggle_mode
l <C-a> + / h <C-x> -generator.increase / generator.decrease
xgenerator.toggle_option
<CR>generator.use (popup) / generator.toggle_option (tab, on a checkbox)

health

KeysAction
<Tab> / <S-Tab>health.next_category / health.prev_category
<CR>health.open_item
e / gpitem.edit / item.generate_password
rhealth.rerun

quick_open

KeysAction
<C-n> <C-j> <Down> / <C-p> <C-k> <Up>quick_open.next / quick_open.prev
<CR>quick_open.accept
<C-y> / <C-u> / <C-t>copy password / username / code
<C-e>quick_open.edit
<Esc> <C-c>app.cancel

cmdline

KeysAction
<CR>cmdline.execute
<Tab> / <S-Tab>complete next / previous
<Up> <C-p> / <Down> <C-n>history previous / next
<Esc> <C-c>app.cancel

picker, confirm, unlock, settings, keymap_editor, help

ContextKeys → action
pickertype to filter; <C-n> <Down> / <C-p> <Up> next/prev; <CR> accept; <Esc> cancel. These come from the keymap, so they can be rebound. Lists that are not keymap contexts (export format, import preview, password history, login choices) also take <C-n> / <C-p> beside the arrows and j / k. Small pickers (item type) also accept single-letter accelerators shown in the list (l login, n note, c card, i identity, s SSH key).
confirmy / <CR> (only when the default is yes) → yes; n / <Esc> → no. Destructive dialogs require typing a word instead.
unlock<CR> submit; <C-o> log out; <C-c> quit. The master password re-prompt shares this context but <C-c> cancels it, like <Esc>. The login, start-up and error screens are not keymap contexts; <C-c> quits from them too.
settingsmovement, also <C-n> / <C-p>; <CR> activate; l <C-a> / h <C-x> next/previous value; r reset to default; q <Esc> close.
keymap_editormovement, also <C-n> / <C-p>; / filter; <CR> c rebind; a add key; dd remove key; r reset action; R reset all; <Tab>/<S-Tab> context; <C-s> save; q close.
helpmovement, also <C-n> / <C-p>; / search; q <Esc> close.

Command line

: opens the command line at the bottom (cmdline row). Completion: commands first, then arguments (themes, folders, item types, settings and their values, export formats, import formats, filesystem paths with ~ expansion). History is kept between sessions. Unknown commands print E492: Not an editor command: <text> (Vim’s message).

CommandAction
:q[uit]app.quit (in editor: editor.close)
:q!editor.discard in editor, otherwise app.quit_force
:qa[ll] / :qa!app.quit / app.quit_force
:w[rite]editor.save (keymap editor: keymap.save)
:wq :xeditor.save_close
:e!editor.reload
:{n}go to item number n in the list
:new [login|note|card|identity|ssh]item.new
:edit / :edit jsonitem.edit / item.edit_json
:delete / :delete!item.trash / item.delete
:restoreitem.restore
:favitem.favorite
:move [folder]item.move
:copy <field>item.copy <field>
:open [n]item.open_uri
:historyitem.history
:gen [length] / :gen phrase [words]generate with these options and copy
:folder new|rename|delete [name]folder.*
:sort name|modified|created|type[!]sort.set (! reverses)
:theme [name]theme.set / theme.pick
:setapp.settings
:set opt=value / :set opt? / :set opt! / :set opt&set / show / toggle boolean / reset
:map [ctx] [keys] [action…] / :unmap ctx keyssession mappings (§6.6)
:keymapapp.keymap
:sync / :sync!vault.sync / vault.sync force
:lock / :logoutvault.lock / vault.logout
:export [json|encrypted|account|csv] [path]vault.export (opens the wizard pre-filled)
:import [format] [path]vault.import (wizard pre-filled)
:healthhealth.open
:trashsidebar filter Trash
:empty-trashtrash.empty
:messagesapp.messages
:help [topic]app.help
:doctorapp.doctor

:set option names are the config keys without the section where unambiguous (:set theme=nord, :set clipboard_timeout=20s, :set animations!); fully qualified names always work (:set ui.animations=false).

Search language

Used by / (inside the current sidebar filter) and by quick open (whole vault, trash excluded unless is:trash).

  • Tokens are separated by spaces; "quoted phrases" keep spaces; - negates a token or qualifier.
  • Free text is fuzzy-matched with nucleo-matcher (smart case, Unicode normalisation) against: name (weight 1.0), username (0.7), URI hosts (0.6), custom field names (0.4), notes (0.3, only when vault.search_notes = true). Item score = best weighted score; ties: favorites first, then items opened in this session (most recent first), then name.
  • Matched characters of the name are highlighted with the match token.
QualifierValues
type:login/l, note/n, card/c, identity/i, ssh/s
folder:case-insensitive prefix of the folder path (folder:work/proj), none
is:fav, trash, reprompt, weak, reused, exposed, old, insecure (the last five use the latest health run; without one they are ignored and the statusline says “Run Health first”)
has:totp, passkey, fields, notes, uri, history, attachments
uri:substring of any URI
user:substring of the username
name:substring of the name

Examples: git -type:note, has:totp folder:work, is:reused uri:google, "home router".

Default configuration

This is the configuration lazybw uses when ~/.config/lazybw/config.toml does not set an option. lazybw config --print-default prints the same file. See Configuration for how to change it.

# lazybw configuration
# Location: $XDG_CONFIG_HOME/lazybw/config.toml (usually ~/.config/lazybw/config.toml)
# Durations: "500ms", "30s", "15m", "2h", "365d", or "never" where allowed.

[bw]
path = ""                       # path to the Bitwarden CLI; empty = $LAZYBW_BW, then $PATH
transport = "fd"                # "fd" (private socket, recommended) or "unix" (socket in $XDG_RUNTIME_DIR)
inherit_session = false         # reuse BW_SESSION from the environment if it is set

[ui]
theme = "tokyonight-night"
transparent = false
icons = "nerdfont"              # nerdfont | unicode | ascii
animations = true
list_density = "comfortable"    # comfortable | compact
statusline_separator = "arrow"  # arrow | round | slant | none
show_hints = true               # key hints next to the active field
date_format = "relative"        # relative | iso
mouse = true

[security]
clipboard_timeout = "30s"       # "never" keeps copied values
clear_clipboard_on_exit = true
auto_lock = "15m"               # "never" disables
reveal_timeout = "20s"
hide_on_focus_lost = true
reprompt_grace = "0s"           # how long a master password re-prompt stays valid
totp = true                     # show verification codes computed from authenticator keys

[vault]
sync_interval = "10m"           # "never" disables background sync
sort = "name"                   # name | modified | created | type
search_notes = false
show_empty_types = false
confirm_trash = false           # dd asks before moving to trash (it can always be undone with u)

[generator]
type = "password"               # password | passphrase
length = 24
uppercase = true
lowercase = true
numbers = true
special = true
min_numbers = 1
min_special = 1
avoid_ambiguous = false
words = 5
separator = "-"
capitalize = true
include_number = true

[health]
check_exposed = true            # Have I Been Pwned (k-anonymity) through bw report
old_password_age = "365d"

[editor]
command = ""                    # empty = $VISUAL, then $EDITOR, then vi

[keys]
leader = "<Space>"
timeoutlen = "1000ms"
which_key_delay = "300ms"
defaults = true
# Per-context overrides, e.g.:
# [keys.list]
# "<Del>" = "item.trash"
# "dd" = "noop"

Command-line options

lazybw                          start the interface
lazybw --version                print the version
lazybw --help                   print help

lazybw doctor

Prints what lazybw found: the bw path, its version and the features it enables, the login state, the clipboard backend, the terminal size and colour depth, and the configuration, state and log paths. Inside tmux it also reminds you about extended-keys.

lazybw keys

lazybw keys                     every binding, grouped by context
lazybw keys --context list      only one context
lazybw keys --defaults          the built-in defaults, ignoring your config
lazybw keys --check             validate the bindings; exit code 1 on errors

The columns are the context, the keys, the action, its description and where the binding comes from (default or user).

lazybw themes

lazybw themes                   list the built-in themes
lazybw themes --preview nord    print the colours of one theme

lazybw config

lazybw config --path            print the path of config.toml
lazybw config --print-default   print the default configuration
lazybw config --check           validate config.toml; exit code 1 on errors

Environment variables

VariableEffect
LAZYBW_BWpath to bw, used when bw.path is empty
BW_SESSIONreused only with bw.inherit_session = true
BITWARDENCLI_APPDATA_DIRpassed on to bw (its data directory)
VISUAL, EDITORthe editor for Ctrl-e and gE, unless editor.command is set
XDG_CONFIG_HOME, XDG_STATE_HOME, XDG_RUNTIME_DIRwhere lazybw keeps its files
COLORTERMtruecolor or 24bit turns on 24-bit colour
NO_COLORturns colours off

Signals

SIGUSR1 locks the vault (pkill -USR1 lazybw). SIGTERM and SIGHUP quit cleanly.