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>/cmdlineandps 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 everybwinvocation.- 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
--sessionvalues.
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_SESSIONinto 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_SESSIONin.bashrcor.profilewhere 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?