# 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)