vault-gate

https://jphein.github.io/vault-gate/ · GitHub

Claude Code hook that auto-unlocks your Vaultwarden vault whenever a bw command is about to run. When the vault is locked, it pops a Ghostty window where you enter your master password; the $BW_SESSION token is captured and cached — by default in GNOME Keyring — so subsequent bw calls in the session just work.

What’s in here

Token storage

Configured via ~/.config/vault-gate/config:

# keyring — GNOME Keyring via secret-tool (recommended, default)
# file    — plaintext file in $XDG_RUNTIME_DIR (mode 700, RAM-backed)
STORAGE=keyring
Backend Where On disk? Cleared on logout?
keyring GNOME Keyring (libsecret) No Yes (session-tied)
file $XDG_RUNTIME_DIR/bw-session-token No (tmpfs) Yes

Both backends avoid /tmp, which is world-readable by directory even when the file itself is mode 600. keyring is default; if secret-tool isn’t installed, both scripts automatically fall back to file with a warning.

Install secret-tool on Ubuntu/Debian:

sudo apt install libsecret-tools

Why a wrapper is needed

Claude Code’s PreToolUse hooks can only allow or block a tool call (exit 0 / exit 2); they cannot mutate the environment of the subsequent process. So when vault-gate.sh writes the unlocked BW_SESSION into GNOME Keyring (or $XDG_RUNTIME_DIR), the next bw invocation still has no BW_SESSION in its env — it queries the daemon, gets back locked, and the cache is effectively useless. The shim at ~/.local/bin/bw reads the cached token and exports BW_SESSION itself before exec’ing the real bw, closing the loop. A bash function in .bashrc would be cleaner but won’t work — Claude Code’s Bash tool runs bash -c '…' (non-interactive, non-login), which doesn’t source .bashrc at all.

How it integrates with Claude Code hooks

Claude Code passes hook input as JSON on stdin. The hook command in settings.local.json parses it with jq:

TOOL_INPUT="$(jq -r '.tool_input.command // ""' 2>/dev/null || echo "")"
if echo "$TOOL_INPUT" | grep -qP '(?<![/\w.-])bw\s|["\x27]bw\s'; then
    bash "$HOME/.claude/scripts/vault-gate.sh"
fi

install.sh adds this entry if it’s not already present.

Install

git clone https://github.com/jphein/vault-gate.git ~/Projects/vault-gate
cd ~/Projects/vault-gate
./install.sh
# restart Claude Code

install.sh is idempotent — safe to re-run after updates.

Gotchas

Troubleshooting

License

MIT