Skip to content

Releases: DrewCarlson/bwx-cli

v2.3.1

27 Apr 04:37
Immutable release. Only release title and notes can be modified.
v2.3.1
893d05f

Choose a tag to compare

[2.3.1] - 2026-04-27

  • Fix bwx touchid enroll failing with SecItemAdd: status -34018
    on signed builds.
    2.3.0 moved Touch ID wrapper-key storage to the
    data-protection keychain, but the Developer-ID signing path didn't
    carry the entitlements that scope binaries into a DP keychain
    group, so every SecItem* call returned errSecMissingEntitlement.
    scripts/sign-macos.sh now embeds application-identifier and
    keychain-access-groups = ["TEAMID.bwx"] on the
    hardened-runtime path (Team ID extracted from the identity string,
    override with TEAM_ID=…). At runtime src/touchid/keychain.rs
    checks its own application-identifier entitlement via
    SecTaskCopyValueForEntitlement and only requests the DP keychain
    when present; cargo-install, ad-hoc, Apple Development, and
    unsigned local builds transparently fall back to the legacy file
    keychain (which works without entitlements).

v2.3.0

27 Apr 03:48
Immutable release. Only release title and notes can be modified.
v2.3.0
02da89e

Choose a tag to compare

[2.3.0] - 2026-04-27

  • Faster --folder lookup in bwx add/generate. When passeing
    --folder <name>, the CLI used to decrypt every folder name one
    IPC at a time just to check whether the requested folder already
    existed. Folder lookup now batches into a single DecryptBatch,
    one IPC regardless of folder count.
  • Faster bwx add/generate/edit. New EncryptBatch IPC
    (mirrors DecryptBatch): the agent encrypts a vector of
    plaintexts in one shot and returns per-item results. bwx add
    and bwx generate previously fired one IPC each for name,
    username, password, notes, and every URI; bwx edit fired one
    per modified field. All three now stage their plaintexts through
    a local EncryptBatcher helper and flush a single round trip.
    Minor protocol bump — the new variants are additive, so older
    agents still work for any command that doesn't actually use
    EncryptBatch, but add/generate/edit require both binaries
    on this version.
  • Faster full-detail decrypt for bwx get --full/history/code.
    The full-cipher decrypt path (decrypt_cipher /
    decrypt_cipher_using_search) previously fired one IPC per
    cipherstring — folder, notes, every history password, both halves
    of every custom field, and every variant-specific field (login
    password/totp/URIs, identity address fields, card numbers, SSH
    key parts). An entry with several history rows and custom fields
    could rack up 20+ synchronous round trips. Both functions now
    stage every field into one DecryptBatch via a shared Batcher
    helper and assemble results from the response.
  • Faster bwx get/code/edit/remove/history/search on
    large vaults.
    find_entry and search previously made one IPC
    round-trip per cipherstring per entry (name + username + folder +
    notes + every URI + every custom field), which scaled as
    O(entries × fields) and could easily reach hundreds of round-trips
    on a sync. Both now route through a new decrypt_search_ciphers
    helper that bundles every field into a single DecryptBatch IPC,
    with the agent decrypting them in one shot.
  • Configurable diagnostic logging. New logging config key
    (bwx config set logging on|off, default on) toggles a single
    bucket of diagnostic output written to stderr — error/warning
    messages plus debug records used to trace internal operations and
    time long-running steps. When off, bwx and bwx-agent emit
    nothing on stderr. Output is restricted to our crates so
    third-party libraries don't leak through; RUST_LOG, when set,
    overrides the configured level for ad-hoc debugging. The new
    bwx::debug_time! macro is zero-cost when logging is off (no
    Instant, no string formatting).
  • macOS-only commands no longer appear in --help on other
    platforms.
    bwx touchid {enroll,disable,status}, bwx setup-macos,
    and bwx teardown-macos are now #[cfg(target_os = "macos")]-gated
    in the clap Opt enum, so Linux/BSD builds simply don't list them
    rather than listing them with a stub that errors at runtime. The
    "(macOS only)" suffix has been dropped from the surviving help
    text since it's now redundant.
  • Touch ID wrapper key now lives in the data-protection keychain.
    All SecItem* calls in src/touchid/keychain.rs pass
    kSecUseDataProtectionKeychain = true. Items are scoped by the
    binary's team-identifier rather than via per-binary login-keychain
    ACLs, so reinstalling or upgrading a Developer-ID-signed bwx no
    longer triggers the "bwx wants to access the keychain" prompt and
    there is no "Always Allow" ACL to manage.

v2.2.1

26 Apr 05:43
Immutable release. Only release title and notes can be modified.
v2.2.1
1c3113e

Choose a tag to compare

[2.2.1] - 2026-04-26

  • Same-team peer verification on the agent socket (macOS). When
    the agent itself is signed with a Team Identifier (Developer ID or
    Apple Development), it now requires connecting clients to be signed
    by the same team via a SecCodeCheckValidity requirement string.
    Closes the "another process running as my uid that's signed by some
    other identity". Ad-hoc and unsigned agent builds (local
    cargo install, forks without a paid Apple cert) keep the prior
    same-uid-only behavior, so dev workflows aren't disrupted.
  • Rejected agent connections now send a Response::Error before
    closing instead of dropping silently, so the CLI fails fast with a
    real error message. bwx stop-agent (and any other path that ends
    up sending Quit) now bounds its wait_for_exit poll at 2 seconds
    rather than waiting forever.
  • Fix macOS releases getting killed at exec time. 2.2.0 binaries
    were signed with a keychain-access-groups entitlement that AMFI
    treats as restricted; without a provisioning profile (which a bare
    CLI Mach-O can't carry) the kernel rejects the signature and SIGKILLs
    the process before main runs. Drop the entitlement; Touch ID
    enforcement stays put — the agent's presence check still fires
    before the wrapper key is released, the only loss is the
    Keychain-side biometric ACL which was never actually being applied
    on shipped builds anyway.
  • bwx config show/set/unset now offer the valid configuration keys
    via shell completions and reject unknown keys at the CLI parse step.
    bwx config unset sync_interval resets it to the default instead of
    erroring.

v2.2.0

26 Apr 04:03
Immutable release. Only release title and notes can be modified.
v2.2.0
dfab707

Choose a tag to compare

[2.2.0] - 2026-04-26

  • Cache Config::load() once per bwx invocation; trims a few ms of
    redundant disk + JSON parse from every command that touches the
    vault.
  • Reuse the search-cipher plaintext when finalising the matched entry
    in find_entry, so get/code/exec skip the redundant
    decrypt-via-IPC of name, folder, notes, and login username.
  • Skip the second KDF run during bwx login. The agent now reuses the
    Identity produced while authenticating to unlock the vault,
    shaving 100-500ms off Argon2id-backed accounts on first login.
  • Cache the agent protocol-version probe within a single CLI
    invocation, so commands that re-enter ensure_agent (e.g.
    --clipboard) don't pay for a redundant IPC round-trip.
    stop_agent() now invalidates that cache so a later
    ensure_agent re-verifies a fresh agent.
  • Memoise compiled URI-match regexes during entry lookup. The find
    loop runs the same patterns against every cipher several times per
    search; compiling once per pattern keeps regex-heavy bwx get
    callers off the hot path.
  • bwx list now decrypts the whole vault in a single DecryptBatch
    IPC instead of one round-trip per field. Adds Action::DecryptBatch
    and Response::DecryptBatch to the agent protocol; per-item failures
    are reported back to the caller without aborting the batch.
  • Reuse a single UnixStream for every IPC inside a bwx invocation
    instead of opening a fresh connection per action. Cached socket is
    cleared on send/recv failure (transparent reconnect) and on Quit.
  • Cap DecryptBatch requests at 10,000 items and forward only the
    top-level error context for per-item failures, so the agent doesn't
    echo wrapped error chains over IPC.
  • Release workflow attestation step migrated from
    actions/attest-build-provenance to actions/attest@v4.1.0 (the
    upstream-recommended target for new implementations; the old action
    is now just a passthrough wrapper).
  • Wire-format change. CLI ↔ agent IPC moves from line-delimited
    JSON to length-prefixed MessagePack (rmp-serde). 4-byte big-endian
    payload size, 16 MiB cap on both directions, same Request/
    Response enum shapes. Smaller and faster to encode/decode for
    large payloads (bwx list, DecryptBatch). Run bwx stop-agent
    after upgrading
    so the new CLI doesn't try to talk to a still-
    running pre-2.1.1 agent.
  • Agent now caps each request read at 30 seconds and exits the
    connection cleanly on timeout, so a peer that sends a length prefix
    and then stalls can't pin a tokio task forever.

v2.1.0

25 Apr 19:01
Immutable release. Only release title and notes can be modified.
v2.1.0
4a39530

Choose a tag to compare

[2.1.0] - 2026-04-26

Added

  • bwx exec --env VAR=ENTRY[#FIELD] -- <cmd>. Run a child process
    with vault fields bound to environment variables. Each --env flag
    resolves a vault entry (by name, UUID, or URI) and an optional field
    (defaults to password); the value is passed via execve() only and
    the parent zeroizes its in-process copy as soon as the child has been
    spawned, so secrets never touch disk. The exec'd child's exit code
    (or 128 + signal on Unix signal termination) is propagated. Drop-in
    replacement for direnv + plaintext .env files for CLI workflows
    (bwx exec --env DB_URL=db/prod#uri -- terraform apply).

v2.0.2

25 Apr 11:12
Immutable release. Only release title and notes can be modified.
v2.0.2
224c087

Choose a tag to compare

[2.0.2] - 2026-04-25

Project renamed from rbw to bwx-cli. The binaries are now bwx
and bwx-agent; XDG dirs move from ~/.config/rbw etc. to
~/.config/bwx; the macOS Keychain service is "bwx"; LaunchAgent
labels are drews.website.bwx.*; env vars use a BWX_ prefix
(BWX_PROFILE, BWX_TTY, …). Existing installs need to migrate
config/cache dirs and re-enroll Touch ID.

First-class macOS support, an extensive security hardening pass, and a
substantial dependency-tree reduction. Linux/BSD users get a drop-in
replacement for upstream rbw with the same hardening applied.

Added

  • Touch ID unlock (macOS). bwx touchid enroll/disable/status wrap
    the vault keys under a Keychain-held wrapper key so biometry can replace
    the master password. Three signing tiers (Developer ID, Apple Development,
    ad-hoc) are detected at runtime and use the strongest available ACL.
  • Per-operation Touch ID gate. touchid_gate = off | signing | all
    optionally requires a biometric prompt before each vault read or SSH
    sign, coalesced to one prompt per bwx <command> via a session token.
  • Native macOS dialogs. Master password, 2FA codes, and SSH-sign
    confirmation render as Aqua dialogs via osascript when pinentry isn't
    available; toggled by macos_unlock_dialog.
  • bwx setup-macos / bwx teardown-macos. Install/remove two
    LaunchAgents — one for bwx-agent keepalive, one to publish
    SSH_AUTH_SOCK into launchd's environment so GUI apps inherit it.
  • Built-in SSH agent + git commit signing. Serves SSH keys stored as
    Bitwarden "SSH Key" items. bwx ssh-public-key, bwx ssh-allowed-signers,
    and bwx ssh-socket support gpg.format = ssh workflows.
  • ssh_confirm_sign. Optional pinentry CONFIRM dialog before each SSH
    signature, with the requesting program's name + PID surfaced in the
    prompt so the user knows which client is asking.
  • End-to-end test suite. A vaultwarden-spawning harness with 43
    scenarios, run on Ubuntu and macOS in CI.

Changed

  • Replaced 20+ third-party crates with hand-rolled equivalents where the
    upstream surface was overkill for our use: axum → raw tokio HTTP
    parser for the SSO callback, region → direct rustix mlock/munlock
    (also fixes the musl munlock panic on RLIMIT_MEMLOCK-constrained
    CI runners), humantime, textwrap, terminal_size, is-terminal,
    percent-encoding, uuid, directories, libc (direct usage),
    serde_path_to_error, block-padding, arrayvec, serde_repr,
    totp-rs, base32, anyhow, daemonize, env_logger, hkdf,
    clap_complete_nushell/clap_complete_fig, futures-channel,
    futures. open is now gated behind the optional sso-browser
    feature.
  • Tokio feature set narrowed from "full" to the actually-used subset.

Security

  • All on-disk state forced to 0o600 / 0o700. config.json, db.json,
    touchid.json, device_id, the pidfile, and the agent socket dir are
    re-tightened on every write — explicit set_permissions in addition to
    OpenOptions::mode, since the latter only applies on file creation.
  • Wrapper seed kept in locked::Vec end-to-end. Touch ID enroll +
    unlock paths never materialize the 64-byte seed in plain heap or on
    the stack; keychain::load returns locked::Vec directly.
  • Pinentry stdout zeroized. osascript / Assuan output buffers are
    scrubbed before the parent Output drops, so a typed master password
    doesn't linger in the heap.
  • SSH key plaintext minimized. ssh-agent Sign handler decrypts the
    private key only after Touch ID + pinentry CONFIRM succeed; cancel
    paths leave no key material in memory.
  • SSO callback hardened. State-mismatch error no longer embeds the
    64-char OAuth state token. Raw tokio listener replaces axum (smaller
    attack surface, no untrusted body parsing).
  • Agent IPC hardened. Per-connection getpeereid / SO_PEERCRED
    check rejects cross-uid clients on shared hosts. Both control and
    ssh-agent sockets bind atomically (tmp + rename) instead of unlink +
    bind. Request and response framing capped at 16 MiB.
  • MAC verify made explicit. CipherString verification now uses
    hmac::Mac::verify_slice (constant-time via subtle::ConstantTimeEq)
    with a comment documenting the guarantee.
  • Touch ID session bookkeeping fixed. Sessions are recorded only
    after a successful unlock, not on bare presence, so a failed unwrap
    doesn't leave a usable auth window for the TTL.
  • SIGTERM/SIGINT zeroize agent state. In-memory keys are dropped
    through their mlock/zeroize Drop impls before exit, instead of
    living in kernel buffers until reaping.
  • pwgen returns locked::Password. Generated passwords are
    mlocked + zeroized on drop in the immediate caller's scope.

Install

  • macOS: git clone … && ./scripts/install.sh && bwx setup-macos. The
    install script auto-picks the strongest signing identity available
    (Developer ID > Apple Development > ad-hoc) and applies the matching
    Keychain ACL strategy.