Adds the dockerized bw deployment in production use on the TSGCOO orchestration host since 2026-08-13: pinned debian-slim image carrying the pre-compiled bw binary, an in-container auth lifecycle entrypoint (config, API-key login, unlock, sync), a transparent host wrapper, and a one-command installer. ADR-002 records the decision and supersedes ADR-001 for BW CLI purposes: hosts keep zero language runtimes. Known caveat documented: the upstream "native" binary is a Node.js SEA, so Node is embedded in the image though absent from all hosts. Shellcheck clean (zero warnings incl. info-level).
3.2 KiB
ADR-002: Container-Based Bitwarden CLI (No Host Node.js)
Status
Accepted (supersedes ADR-001 for BW CLI purposes)
Context
ADR-001 selected a hybrid Node.js version-management strategy because the
Bitwarden CLI (bw) is distributed via npm and therefore requires a Node.js
runtime on every consuming host. For TSYS hosts operating under CMMC L3 /
ITAR / STIG alignment, any host-side language runtime is an attack surface
and a compliance finding. The KNEL/TSYS baseline ("Docker for everything")
also forbids host language toolchains.
Bitwarden additionally publishes the CLI as a pre-compiled single binary.
Distributing that binary inside a minimal pinned container gives every host
transparent bw access with zero host-side runtimes.
Decision
Run the official pre-compiled bw binary inside a pinned Docker container,
exposed to users as a transparent bw wrapper at ~/.local/bin/bw.
Components (all in this repo):
| File | Role |
|---|---|
docker/bw-native/Dockerfile |
debian:12-slim + ca-certificates + bw binary, pinned 2026.7.0 |
bin/bw-entrypoint.sh |
In-container auth lifecycle: config server, API-key login, unlock, sync, exec |
bin/bw-cli.sh |
Host wrapper: docker run with volume-persisted session, tsys- container prefix |
bin/bw-install.sh |
One-command installer for the wrapper + image |
Credentials live only in ~/.config/bw/env (single-quoted values, mode 600)
per the org-wide "no secrets on disk except BW access info" rule. The
Vaultwarden API key is used for login (no TOTP interaction needed); the
master password unlocks the vault; bw sync runs after every login so
multi-client vault state stays coherent.
Known Caveat
The Bitwarden "native" Linux binary is actually a Node.js SEA (Single
Executable Application): it embeds a Node.js runtime and can print Node
errors on crash. Node.js is absent from every host, which satisfies the
host-hygiene goal, but the "zero Node.js anywhere" stretch goal is not met.
Alternatives (Rust vaultwarden clients such as rbw) were considered and
rejected for now due to Vaultwarden API compatibility gaps. Revisit if a
mature Rust client emerges; the wrapper isolates users from this swap.
Consequences
Positive
- Zero language runtimes on hosts (CMMC/ITAR/STIG-friendly host hygiene)
- Identical bw behavior across all TSYS hosts; version pinned in one Dockerfile
- Session persistence via named Docker volume; no state divergence when wrapper is used consistently
- Vendoring into shell frameworks is one installer invocation
Negative
- Requires Docker on every consuming host (accepted: it is an org baseline)
- SEA caveat above (Node embedded inside the container image)
Verification
Deployed and verified on the TSGCOO orchestration host against
https://pwvault.turnsys.com (account coo@turnsys.com): status, list,
generate, get password/totp/item, create/edit items. Shellcheck clean at
zero warnings including info-level. In production use since 2026-08-13.
Decision Date: 2026-08-14 Decision Makers: VP TechOps (proposed), TSGCOO (implemented) Related: ADR-001 (superseded for BW CLI purposes; MISE guidance remains valid for projects that genuinely require host Node.js)