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