Bitwarden CLI session keys: the BW_SESSION footgun in agent automation

The Bitwarden CLI (bw) unlocks a vault once and returns a session key. Every subsequent call needs that key, normally via the BW_SESSION environment variable. In agent automation — cron jobs, CI pipelines, AI coding agents — there is a constant temptation to shortcut it: bw get item foo --session "$BW_SESSION", or bw unlock --raw piped into a wrapper script. That shortcut is a footgun, because a session key on the command line is a session key in the open.

Why argv is the wrong place

Process command lines are not private on a shared machine:

  • /proc/<pid>/cmdline and ps aux — any process running as the same user can read another process’s arguments. A compromised background process or a curious co-worker’s session can dump every bw invocation.
  • Process supervisors and dashboards — systemd units, container runtimes, and monitoring tools frequently log the full command line of every process they manage.
  • CI and shell history — wrapper scripts, debug output, and history files preserve whatever was typed, including --session values.

A Bitwarden session key is a bearer credential: whoever holds it can read the vault for the session’s lifetime. It is not a password you can casually rotate — it is tied to an active unlock. Once it lands in a process list or a log file, the exposure is real and the remediation is awkward.

The environment variable, done right

The correct channel is the process environment, and only there:

  • Export BW_SESSION into the consumer’s environment — never pass it as an argument. Environment variables are inherited by child processes but are not part of the command line.
  • Scope it narrowly. Do not put BW_SESSION in .bashrc or .profile where every interactive shell inherits it. Set it for the specific agent invocation that needs the vault.
  • Keep it out of history. Prefer reading the key from a 0600 file or resolving it inside the automation, rather than typing export BW_SESSION=$(bw unlock --raw) into an interactive shell.
  • Fail closed. When the session is invalid, the tool should error out — not fall back to an interactive master-password prompt. An agent cannot answer that prompt; it hangs, retries, or worse, surfaces the prompt in a channel where a human might be tricked into typing a master password.

Why “fails closed” matters for agents

Agent automation is non-interactive by definition. A stale session should produce a clear, loggable error: “invalid session, re-unlock required.” That error is actionable — it tells you the unlock step needs to run again. Interactive fallback, by contrast, fails silently from the agent’s point of view: the process blocks, the job times out, and the only trace is a vague failure hours later.

Fail-closed behavior also gives you a detection signal. Failed unlocks are visible in logs, so a broken pipeline or an expired session becomes a monitored event instead of a mystery.

The pattern in practice

The durable pattern for Bitwarden-backed agent automation: unlock once, transfer the session key through the environment of the consuming process only, never through argv, and treat an invalid session as a hard error with a clear message. If you are building the integration yourself, audit your wrapper scripts for --session flags and bw calls that echo arguments.

trustless reads the Bitwarden session key from the BW_SESSION environment variable only — never from argv — and fails closed when the session is invalid. The backend design is documented in the architecture page.

Where does BW_SESSION live in your setup right now — in an environment, or in a process list?