Skip to content

DrewCarlson/bwx-cli

Repository files navigation

bwx

Bitwarden in your terminal, without the master-password prompt every time. bwx keeps your vault unlocked in a background agent — like ssh-agent for your passwords.

Fork of rbw that adds first-class macOS support (Touch ID unlock, native dialogs, signed + notarized binaries, built-in SSH agent for git commit signing, one-shot setup). Drop-in replacement on Linux/BSD.

Features

  • Persistent agent. Vault keys live in memory until lock_timeout of inactivity.
  • Touch ID unlock (macOS). Enroll once, and biometry replaces the master password. The password is only re-entered at enrollment time or when biometry is invalidated.
  • Per-operation biometric gate. Optionally require Touch ID before each vault read or SSH sign, with one prompt per bwx <command>.
  • Native macOS prompts. Master password + 2FA code entry render as system Aqua dialogs; pinentry isn't required.
  • SSH agent built in. Serve vault-stored SSH keys, including git commit/tag signing via gpg.format = ssh.
  • One-shot macOS setup. bwx setup-macos installs the LaunchAgent that keeps bwx-agent alive and registers the SSH-agent socket so terminal sessions (and the GUI apps they launch) can use it via a one-line shell-rc export.

Install

macOS

brew install DrewCarlson/tap/bwx-cli
bwx setup-macos               # LaunchAgent that keeps bwx-agent alive

Everywhere else

Channel Command
crates.io (any platform) cargo install --locked bwx-cli (binaries are bwx / bwx-agent)
Arch (AUR — release) yay -S bwx-cli (or any AUR helper)
Arch (AUR — git) yay -S bwx-cli-git
Nix flake nix profile install github:drewcarlson/bwx-cli
Debian / Ubuntu (.deb) download from GitHub Releasessudo dpkg -i bwx-cli_*_amd64.deb
Fedora / RHEL (.rpm) download from GitHub Releasessudo dnf install ./bwx-cli-*.x86_64.rpm
Standalone tarball download from GitHub Releases, extract, put bwx/bwx-agent on PATH
From source (any) git clone … && ./scripts/install.sh (auto-runs scripts/sign-macos.sh on macOS)

Each tagged release builds Linux x86_64 + aarch64 (glibc and musl) and macOS arm64 + x86_64 artifacts, attached to the GitHub Release. On Linux you'll also want pinentry from your distro so the master-password prompt has a UI.

Usage

First-time setup

bwx config set email you@example.com
bwx config set base_url https://vault.example.com   # self-hosted only

bwx register   # bitwarden.com only
bwx login      # master password + 2FA
bwx sync

Bitwarden.com users: the official server's bot detection rejects CLI clients that haven't called register once with a personal API key. Self-hosted servers (Vaultwarden, etc.) skip this step.

Reading entries

bwx get github.com           # password by entry name
bwx get <uuid>               # by Bitwarden item UUID
bwx get https://github.com   # by stored URI
bwx get --field totp <name>  # any single field
bwx get --full <name>        # password + fields + notes
bwx get --raw <name>         # JSON
bwx code <name>              # generated TOTP code

The agent auto-unlocks on the first call after bwx login (or after a lock-timeout expiry).

Adding, editing, removing

bwx add <name>               # password from $EDITOR or stdin
bwx edit <name>              # opens the entry in $EDITOR
bwx remove <name>

Locking the vault

bwx lock                     # drop keys from memory immediately
bwx unlocked                 # exit 0 if unlocked, 1 if locked

The vault auto-locks after lock_timeout seconds of inactivity (1h by default — configurable). bwx help lists every subcommand.

Touch ID unlock (macOS)

Enroll once:

bwx unlock                  # master password
bwx touchid enroll          # wrap vault keys under a biometric key
bwx touchid status          # confirm

After enrollment Touch ID alone unlocks the vault. The master password is needed again only if you bwx touchid disable, change your enrolled fingerprint set, or re-authenticate with the server.

Optionally prompt Touch ID on each sensitive operation:

bwx config set touchid_gate all       # every vault read + sign
bwx config set touchid_gate signing   # only SSH signs + TOTP codes
bwx config set touchid_gate off       # default

Prompts are coalesced: one bwx <command> triggers one Touch ID dialog regardless of how many internal decrypts it performs.

SSH agent & git commit signing

bwx-agent exposes an SSH agent that serves SSH keys stored in your vault. Store an "SSH Key" item, then:

# Configured automatically by `bwx setup-macos`; explicit equivalent:
export SSH_AUTH_SOCK="$(bwx ssh-socket)"

ssh-add -L                  # list keys
ssh user@host               # authenticate with a vault-stored key

Git commit signing via gpg.format = ssh:

git config --global gpg.format ssh
git config --global user.signingkey "$(bwx ssh-public-key <entry>)"
git config --global commit.gpgsign true
git config --global tag.gpgsign true

# Optional, for `git log --show-signature`:
bwx ssh-allowed-signers > ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers

Require a confirmation prompt before each signature, so a background process can't sign silently while the agent is unlocked:

bwx config set ssh_confirm_sign true

Shell + GUI app inheritance. Modern macOS no longer propagates launchctl setenv into the env of Dock- / Spotlight-launched apps. Add this line to your shell rc so terminal sessions and the apps they launch see the bwx ssh-agent:

export SSH_AUTH_SOCK="$(bwx ssh-socket)"   # ~/.zshrc, ~/.bashrc, …

GUI apps that were already open need to be Cmd-Q'd and relaunched (so their env is re-inherited from the shell) before they pick it up.

IntelliJ IDEs specifically: Settings → Version Control → Git → "Native" (not Built-in). JGit doesn't honor gpg.format = ssh.

Configuration

bwx config set <key> <value>
bwx config show                  # all keys (JSON)
bwx config show <key>            # single value
bwx config unset <key>
Key Default
email Required.
base_url https://api.bitwarden.com Self-hosted server URL.
lock_timeout 3600 Seconds idle → re-lock.
sync_interval 3600 Seconds between auto-syncs. 0 disables.
touchid_gate off off / signing / all.
macos_unlock_dialog true (macOS) Native dialog vs. pinentry.
ssh_confirm_sign false Pinentry CONFIRM before each SSH sign.
pinentry pinentry Pinentry binary to use.

Profiles

Set BWX_PROFILE=<name> to keep an independent vault, config, and agent under that name.

2FA

Supported: Email, Authenticator App, Yubico OTP security key.

Not supported: WebAuthn / Passkey, Duo. Add a supported mechanism alongside them — bwx will use the supported one while your web/mobile clients keep whichever you prefer.

Verifying release artifacts

Every release artifact carries a SLSA build-provenance attestation (signed with the release workflow's GitHub OIDC identity, recorded in the sigstore rekor transparency log) plus a .minisig signature.

# GitHub-native attestation verify. Confirms the artifact was built
# by the bwx-cli release workflow on a tagged commit. Requires `gh`.
gh attestation verify bwx-cli_2.0.0_amd64.deb \
  --repo drewcarlson/bwx-cli

# Minisign — single shipped pubkey at packaging/minisign.pub.
minisign -V -p packaging/minisign.pub \
  -m bwx-cli_2.0.0_amd64.deb

# `SHA256SUMS` covers every file in the release.
sha256sum -c SHA256SUMS

Appendix: macOS internals

Code signing

No paid Apple Developer cert? You're fine. If scripts/install.sh can't find one in your keychain it falls back to ad-hoc signing (codesign -s -) and the agent still works for personal use — Touch ID prompts are enforced by bwx-agent's own LAContext call instead of an OS-level Keychain ACL.

The full picture: cargo install produces unsigned binaries; macOS AMFI kills unsigned processes that touch the Keychain. scripts/install.sh wraps cargo install and runs scripts/sign-macos.sh, which auto-picks the strongest signing identity on your machine:

  1. $IDENTITY env var (explicit override).
  2. Developer ID Application cert (paid Apple Developer program).
  3. Apple Development cert (free via Xcode).
  4. Ad-hoc (-) — no cert required.

The Homebrew install ships pre-signed with Developer ID and is notarized by Apple, so users coming through brew don't need to think about any of this.

Touch ID is enforced by the agent: require_presence runs an LAContext.evaluatePolicy prompt before the wrapper key leaves the Keychain. Item-level biometric ACLs would require a provisioning profile, which a bare CLI Mach-O can't carry, so all three tiers use the same presence-checked path. The Developer ID tier still adds real isolation — Keychain items are scoped to the team's implicit access group, so other-process-same-uid attackers signed by a different identity can't read them.

bwx setup-macos

Installs two LaunchAgents under ~/Library/LaunchAgents/:

  • drews.website.bwx.ssh-auth-sock — runs ~/bin/bwx-set-ssh-sock at login, which calls launchctl setenv SSH_AUTH_SOCK $(bwx ssh-socket). Puts the socket into launchd's environment so GUI apps inherit it.
  • drews.website.bwx.agent — runs bwx-agent --no-daemonize under launchd supervision with KeepAlive. Log output lands in ~/Library/Application Support/bwx/launchd-agent.{out,err}.

bwx teardown-macos unloads both and removes the files.

About

better (unofficial) bitwarden cli with 'Touch ID' support

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages