bw's name search is fuzzy: every agent email contains "coo"
(tsgstaff-coo-*), so `bw get totp "coo Cloudron"` matched 10 items and
errored. get_totp now resolves via get_item_id (exact-name filter)
first, mirroring get_item_password.
Added validate-all-logins.py: per-agent fresh-browser-context login
(shared contexts carry session cookies and hide the login form),
asserts password+TOTP round-trip, then verifies every stored API
credential against its system. First full run: 9/10 PASS.
Known failure: vp-compliance stored password does not match the
account ("Incorrect username or password" pre-TOTP) -- enrollment
typed a different value than stored. Needs Cloudron admin reset,
then update_item and re-validate.
The provisioner's BitwardenHelper.login() was missing the critical
`bw sync` step that the host wrapper includes. Without syncing after
login, the container's local vault cache was empty/stale, causing items
to vanish between container runs. Added sync() call at end of login()
and before list_items().
Also fixed container UID/GID to match host user (1002:1002) for proper
bind-mount access, and added source-code volume mounts for fast iteration.
Verified with 5-phase cross-container persistence test (create in
container A, verify in fresh container B, update in C, confirm in D).
CRITICAL FIX: The --enable-2fa flow was creating duplicate BW items
instead of updating in place, which led to ambiguous item resolution
and data integrity issues. This was a severe failure in core
credential lifecycle operations.
Changes:
- bw_helper.py: Complete rewrite with safety guarantees
- update_item(): modifies existing item in place by ID, preserves
all fields not being updated
- create_item(): refuses to create duplicates (raises if item exists)
- get_item_id(): resolves name to ID, raises on ambiguous matches
- get_item(): returns full item JSON
- NO delete_item method exists by design -- credential deletion
is a manual operation only
- provision-agent.py: --enable-2fa now uses update_item() instead
of create_item() to add TOTP to existing credentials
- Dockerfile: non-root user with correct BW state directory ownership
- docker-compose.yml: bind mount for BW state (proper permissions)
- test_bw_helper.py: 10 tests covering full lifecycle
(create, read, duplicate rejection, update password, update TOTP,
field preservation, no-delete verification)
Tests 1-4 verified passing against live Vaultwarden instance.
- requirements.txt: added pytest
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Replace npm-based @bitwarden/cli with the pre-compiled native Rust bw
binary (v2026.7.0) to eliminate Node.js from the credential management
layer for CMMC/ITAR/STIG audit readiness.
Changes:
- Dockerfile: download native bw binary instead of npm install; add
python3-pip for Playwright dependencies
- bw_helper.py: renamed from bw-helper.py (Python can't import hyphens);
added BW_SERVER config for self-hosted instance; use --passwordfile
for unlock (more reliable with native binary); removed TOTP from
login flow (API key auth does not require it)
- provision-agent.py: pass BW_SERVER env var to BitwardenHelper
- docker-compose.yml: add BW_SERVER env var
- .env.example: add BW_SERVER, document TOTP as optional
Verified: dry-run passes, bw status/auth/generate all work inside
the provisioner container against pwvault.turnsys.com.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2